มีเหตุผลการใช้งานที่แตกต่างกันสำหรับคลาส / อินเตอร์เฟสนามธรรมใน C ++ และ Java


13

ตาม Herb Sutter หนึ่งควรชอบอินเตอร์เฟซที่เป็นนามธรรม (ฟังก์ชั่นเสมือนบริสุทธิ์ทั้งหมด) กับคลาสนามธรรมใน C ++ เพื่อแยกการใช้งานเท่าที่จะทำได้ ในขณะที่ฉันพบว่ากฎนี้มีประโยชน์มากโดยส่วนตัวฉันเพิ่งเข้าร่วมทีมกับโปรแกรมเมอร์ Java จำนวนมากและในโค้ด Java ดูเหมือนว่าแนวทางนี้ไม่มีอยู่จริง ฟังก์ชั่นและการใช้งานของพวกเขามักจะอยู่ในชั้นเรียนนามธรรม ดังนั้นฉันจึงได้รับ Herb Sutter ผิดทั้งหมดแม้แต่กับ C ++ หรือมีความแตกต่างโดยทั่วไปในการใช้ฟังก์ชั่นนามธรรมใน C ++ เทียบกับ Java คลาสนามธรรมพร้อมโค้ดการใช้งานมีเหตุผลใน Java มากกว่าใน C ++ และถ้าใช่ทำไม


1
ฉันมีข้อสงสัยและในที่สุดก็วางไว้ที่นี่เพราะอาจเป็นเพราะหลักการออกแบบบางอย่างที่ฉันขาดเกี่ยวกับ java oo ดังนั้นจึงไม่เกี่ยวกับคำแนะนำทั่วไป แต่เพิ่มเติมเกี่ยวกับสิทธิและความผิดในการใช้ภาษา
มาร์ติน

อินเทอร์เฟซนั้นหมายถึงการเป็นเสมือนอย่างแท้จริง แนวคิดของคลาสนามธรรมคือพวกมันถูกนำไปใช้บางส่วนและมันก็ขึ้นอยู่กับการติดตั้งเพื่อเติมลงในช่องว่างโดยไม่ต้องทำซ้ำรหัสโดยไม่จำเป็น (เช่นทำไมมีการเขียน (byte) และ write (int) ในทุกคลาสย่อยเมื่อคุณ การเรียกคลาสแบบนามธรรม (ไบต์) จากการเขียน (int))

1
อาจเป็นไปได้ที่เกี่ยวข้อง: stackoverflow.com/q/1231985/484230ให้เหตุผลว่าคลาสนามธรรมที่ชอบใน java สำหรับ C ++ ด้วยเหตุผลนี้ดูเหมือนว่าจะไม่เป็นจริงเนื่องจากการมีอยู่ของฟังก์ชั่นฟรีที่สามารถเพิ่มฟังก์ชันการทำงานในระดับอินเตอร์เฟส
Martin

1
ฉันคิดว่ากฎทองคือการ "สร้างคลาสที่ไม่เป็นนามธรรม" แต่นั่นไม่ได้ทำให้ข้อกำหนด "บริสุทธิ์เท่านั้น" หรือ "ว่างเปล่า"
Kerrek SB

1
ถ้ามันเหมาะกับคุณมันเหมาะกับคุณ ฉันไม่เห็นว่าทำไมผู้คนตื่นตระหนกเมื่อรหัสของพวกเขาไม่เป็นไปตามความคิดเห็นล่าสุดอีกต่อไป
James

คำตอบ:


5

OOP มีองค์ประกอบและการทดแทน

C ++ มีหลายมรดกความเชี่ยวชาญแม่แบบฝังและความหมายค่า / ย้าย / ตัวชี้

Java มีการสืบทอดและอินเตอร์เฟสเดียวฝังและซีแมนทิกส์อ้างอิง

วิธีการทั่วไปที่โรงเรียน OOP ใช้ภาษาเหล่านี้คือการใช้การสืบทอดสำหรับการทดแทนวัตถุและฝังเพื่อการแต่งเพลง แต่คุณต้องมีบรรพบุรุษร่วมกันและวิธีการใช้งาน runtime (ใน C ++ เรียกว่าdynamic_castใน Java เป็นเพียงการถามอินเตอร์เฟซจากที่อื่น)

Java ทำสิ่งทั้งหมดนี้ด้วยjava.lang.Objectลำดับชั้นที่รูทของตัวเอง C ++ ไม่มีรูททั่วไปที่กำหนดไว้ล่วงหน้าดังนั้นอย่างน้อยคุณควรนิยามมันเพื่อให้เป็น "รูปภาพ" เดียวกัน (แต่นี่เป็นการ จำกัด ความเป็นไปได้ของ C ++ บางอย่าง ... )

หลังจากนั้นความเป็นไปได้ในการรวบรวม polymorphism เวลา (คิดว่าจะ CRTP) และความหมายตามตัวอักษรสามารถเสนอทางเลือกอื่น ๆ กับวิธีการที่แนวคิดของ "วัตถุ OOP" สามารถพอร์ตในโปรแกรม C ++

คุณสามารถจินตนาการถึงความนอกรีตในการใช้การแปลงฝังและนัยในการจัดการการทดแทนและการสืบทอดส่วนตัวเพื่อจัดการองค์ประกอบ (แน่นอนวิธีนี้มีอายุน้อยกว่าอีก 20 ปีดังนั้นอย่าคาดหวังว่าจะได้รับการสนับสนุนจากชุมชนในการทำเช่นนั้น)

หรือคุณสามารถนึกภาพฐานเสมือนจริงให้กับคลาสทั้งหมด, รูปแบบอินเตอร์เฟส (ไม่มีการนำไปใช้) ไปยังคลาสสุดท้าย (นำไปใช้อย่างสมบูรณ์) โดยผ่านส่วนที่นำไปใช้งานบางส่วนแม้กระทั่งกลุ่มอินเตอร์เฟสโดยใช้ "dominance" เป็นการส่งผ่านจาก - รูปสี่เหลี่ยมด้านขนาน "รูปแบบการสืบทอด

การเปรียบเทียบ OOP กับ java กับ C ++ สมมติว่ามีเพียงวิธีเดียวเท่านั้นและ OOP เท่านั้นที่จำกัดความสามารถของทั้งสองภาษา

การบังคับให้ C ++ ยึดมั่นกับสำนวนการเข้ารหัสของ Java อย่างเคร่งครัดคือการทำให้ C ++ บังคับให้ Java ทำตัวเป็นภาษา C ++ เหมือนกับภาษาที่กำลังทำลาย Java

ไม่ใช่เรื่องของ "ความอ่อนไหว" แต่มี "กลไกการรวม" ที่แตกต่างกันทั้งสองภาษามีวิธีที่แตกต่างกันในการรวมเข้าด้วยกันซึ่งทำให้สำนวนบางอย่างมีประโยชน์มากกว่าในภาษาหนึ่งมากกว่าอีกภาษาหนึ่งและในทางกลับกัน


1
ฉันคิดว่าคำตอบนี้น่าสนใจมากเพราะมันอธิบายคุณลักษณะภาษาเป็นเครื่องมือสำหรับหลักการและการออกแบบเพียงเพื่อช่วยและไม่ใช่หลักคำสอน อย่างไรก็ตามคุณไม่ต้องการรูททั่วไปหากคุณต้องการทำ oo ใน c ++ นี่เป็นความผิดที่ผิดเพียงเพราะคุณมีโอเปอเรเตอร์และแม่แบบ (ซึ่งเป็นทางเลือกที่ทรงพลังมากสำหรับการออกแบบแผนผังหลักของ java ตามที่คุณชี้ไว้) นอกจากนั้นคะแนนของคุณก็คุ้มค่าที่สุดในทุกคำตอบ
มาร์ติน

1
@ Martin: ใน "ความรู้สึกทางเทคนิค" คุณสิทธิกำลัง แต่ถ้าคุณจำเป็นต้องรันไทม์ polymorophism (เพราะชนิดที่แท้จริงของวัตถุ instantiated ขึ้นอยู่กับการป้อนข้อมูลโปรแกรม) "ราก" ( 'a' เป็นบทความไม่ทางลัดสำหรับ " หนึ่งเดียวเท่านั้น ") คือสิ่งที่ทำให้วัตถุ" ลูกพี่ลูกน้อง "ทั้งหมดและลำดับชั้นสามารถใช้เวลาเดินได้ รากที่แตกต่างกันมาที่แตกต่างกันancestriesไม่แต่ละอื่น ๆ ที่เกี่ยวข้อง ไม่ว่าจะเป็น "ดี" หรือ "ไม่ดี" เป็นเรื่องของบริบทไม่ใช่สำนวน
Emilio Garavaglia

นั่นเป็นเรื่องจริง ฉันคิดว่าคุณหมายถึงการปลอมรากหนึ่งทั่วไปสำหรับโปรแกรม c ++ ทั้งหมดและเห็นว่ามันเป็นข้อบกพร่องที่มันไม่ได้อยู่เมื่อเทียบกับจาวา แต่หลังจากการแก้ไขของคุณคุณทำให้จุดค่อนข้างชัดเจน ขอขอบคุณอีกครั้ง
มาร์ติน

12

หลักการมีไว้สำหรับทั้งสองภาษา แต่คุณไม่ได้ทำการเปรียบเทียบที่เป็นธรรม คุณควรเปรียบเทียบคลาส C ++ pure abstract กับอินเตอร์เฟส Java

แม้ใน C ++ คุณสามารถมีคลาสนามธรรมที่มีฟังก์ชั่นบางอย่างที่ใช้งาน แต่มาจากคลาสนามธรรมที่บริสุทธิ์ (ไม่มีการใช้งาน) ใน Java คุณจะมีคลาสนามธรรมที่เหมือนกัน (มีการใช้งานบางอย่าง) ซึ่งสามารถได้มาจากอินเตอร์เฟส (ไม่มีการใช้งาน)


ดังนั้นเมื่อใดที่คุณต้องการคลาสนามธรรมเหนือคลาสอินเตอร์เฟสใน c ++ ฉันเลือกใช้อินเตอร์เฟสและฟังก์ชั่นที่ไม่ใช่สมาชิกใน c ++ เสมอ
มาร์ติน

1
@ มาร์ตินที่ขึ้นอยู่กับการออกแบบ โดยทั่วไปมักชอบอินเตอร์เฟซ แต่กฎ " เสมอ " มีข้อยกเว้น ...
Luchian Grigore

จริงเพียงพอ แต่ในจาวาโค้ดฉันเห็นคลาสนามธรรมเป็นตัวแทนส่วนใหญ่ อาจเป็นเพราะความจริงที่ว่าฟังก์ชั่นฟรีที่ทำงานบนอินเตอร์เฟสไม่สามารถใช้กับ java ได้
มาร์ติน

3
@Martin ฟังก์ชั่นที่ไม่มีค่าใช้จ่ายเป็นไปไม่ได้ที่ Java จุดที่ดี! ตอบคำถามของคุณเอง! คุณสามารถเพิ่มคำตอบด้วยตัวเองฉันคิดว่ามัน
Luchian Grigore

4

โดยทั่วไปหลักการ OO เดียวกันจะเป็นจริงสำหรับ Java และ C ++ อย่างไรก็ตามความแตกต่างที่สำคัญอย่างหนึ่งคือ C ++ รองรับการสืบทอดหลายขณะที่ใน Java คุณสามารถรับมรดกได้จากคลาสเดียวเท่านั้น นี่คือเหตุผลหลักที่ว่าทำไม Java มีส่วนต่อประสานที่ฉันเชื่อว่าเพื่อเสริมการขาดมรดกหลายอย่างและอาจ จำกัด สิ่งที่คุณสามารถทำได้ (เนื่องจากมีการวิพากษ์วิจารณ์มากมายเกี่ยวกับการละเมิดมรดกหลายประการ) ดังนั้นในใจโปรแกรมเมอร์ Java มีความแตกต่างที่แข็งแกร่งระหว่างคลาสนามธรรมและอินเตอร์เฟส คลาสนามธรรมถูกใช้เพื่อแชร์และสืบทอดพฤติกรรมในขณะที่อินเตอร์เฟสใช้เพื่อเพิ่มฟังก์ชันการทำงานพิเศษ จำไว้ว่าใน Java คุณสามารถรับช่วงต่อจากคลาสเดียวเท่านั้น แต่คุณสามารถมีส่วนต่อประสานมากมาย อย่างไรก็ตามใน C ++ คลาสนามธรรมล้วน (เช่น "อินเตอร์เฟส C ++") คือ ใช้เพื่อแบ่งปันและสืบทอดพฤติกรรมที่แตกต่างจากจุดประสงค์ของอินเทอร์เฟซ Java (แม้ว่าคุณยังจำเป็นต้องใช้ฟังก์ชั่น) ดังนั้นการใช้งานจึงแตกต่างจากอินเตอร์เฟส Java


0

บางครั้งมันก็สมเหตุสมผลที่จะมีการติดตั้งเริ่มต้น ตัวอย่างเช่นเมธอด PrintError ทั่วไป (สตริง msg) ที่ใช้กับคลาสย่อยทั้งหมด

virtual PrintError(string msg) { cout << msg; }

มันยังสามารถถูกแทนที่ถ้าจำเป็นจริง ๆ แต่คุณสามารถบันทึกไคลเอนต์ยุ่งยากด้วยการอนุญาตให้พวกเขาเพียงแค่เรียกรุ่นทั่วไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.