มีเหตุผล "ของจริง" ที่ไม่ชอบการสืบทอดหลายครั้งหรือไม่


123

ฉันชอบความคิดที่ว่าจะมีหลายมรดกที่สนับสนุนในภาษา ส่วนใหญ่มักจะลืมมันโดยเจตนาและ "การแทนที่" ที่ควรจะเป็นอินเตอร์เฟส การเชื่อมต่อก็ไม่ได้ครอบคลุมการสืบทอดหลาย ๆ อันเหมือนกันทั้งหมดและข้อ จำกัด นี้บางครั้งอาจนำไปสู่รหัสสำเร็จรูปมากขึ้น

เหตุผลพื้นฐานเดียวที่ฉันเคยได้ยินเรื่องนี้คือปัญหาเพชรกับชั้นเรียนพื้นฐาน ฉันไม่สามารถยอมรับได้ สำหรับฉันมันหลุดออกมามากมายเช่น "เอาละมันเป็นไปได้ที่จะพลาดมันดังนั้นมันจึงเป็นความคิดที่ไม่ดีโดยอัตโนมัติ" คุณสามารถทำอะไรผิดพลาดในภาษาการเขียนโปรแกรมและฉันหมายถึงอะไร ฉันไม่สามารถทำสิ่งนี้ได้อย่างจริงจังอย่างน้อยก็ไม่ใช่โดยไม่ต้องอธิบายให้ละเอียด

เพียงตระหนักถึงปัญหานี้ 90% ของการต่อสู้ นอกจากนี้ฉันคิดว่าฉันได้ยินบางสิ่งบางอย่างเมื่อหลายปีก่อนเกี่ยวกับวัตถุประสงค์ทั่วไปที่เกี่ยวข้องกับอัลกอริทึม "ซองจดหมาย" หรืออะไรทำนองนั้น (เสียงกริ่งดังขึ้นทุกคนหรือไม่)

ปัญหาเกี่ยวกับเพชรปัญหาของแท้ที่ฉันอาจนึกได้คือถ้าคุณพยายามใช้ห้องสมุดบุคคลที่สามและไม่เห็นว่าสองคลาสที่ไม่เกี่ยวข้องกันดูเหมือนว่าในห้องสมุดนั้นมีคลาสพื้นฐานร่วมกัน แต่นอกเหนือไปจาก เอกสารคุณสมบัติของภาษาที่ง่ายอาจทำให้คุณต้องการประกาศความตั้งใจในการสร้างข้าวหลามตัดก่อนที่จะทำการรวบรวมเพชรให้คุณ ด้วยคุณสมบัติเช่นนี้การสร้างเพชรใด ๆ ก็อาจเป็นการจงใจไม่ประมาทหรือเพราะไม่มีใครรู้ถึงความผิดพลาดนี้

เพื่อให้ทุกคนพูดว่า ... มีเหตุผลจริงหรือที่คนส่วนใหญ่เกลียดชังมรดกหลายอย่างหรือว่ามันเป็นเพียงแค่กลุ่มฮิสทีเรียที่ก่อให้เกิดอันตรายมากกว่าดี? มีบางอย่างที่ฉันไม่เห็นใช่ไหม ขอขอบคุณ.

ตัวอย่าง

รถขยาย WheeledVehicle, KIASpectra ขยายรถยนต์และอิเล็กทรอนิกส์ KIASpectra มีวิทยุ เหตุใด KIASpectra จึงไม่มี Electronic

  1. เพราะมันเป็นอิเล็กทรอนิกส์ การสืบทอดกับการจัดองค์ประกอบควรเป็นความสัมพันธ์แบบ is-a กับความสัมพันธ์แบบ a-a

  2. เพราะมันเป็นอิเล็กทรอนิกส์ มีสายไฟแผงวงจรสวิตช์ ฯลฯ ทั้งหมดขึ้นและลงสิ่งนั้น

  3. เพราะมันเป็นอิเล็กทรอนิกส์ หากแบตเตอรี่ของคุณตายในช่วงฤดูหนาวคุณก็มีปัญหาเช่นเดียวกับล้อทั้งหมดของคุณก็หายไป

ทำไมไม่ใช้อินเทอร์เฟซ ยกตัวอย่างเช่น # 3 ฉันไม่ต้องการที่จะเขียนมากกว่านี้และอีกครั้งและผมจริงๆไม่ต้องการที่จะสร้างบางระดับผู้ช่วยพร็อกซี่ที่แปลกประหลาดที่จะทำนี้อย่างใดอย่างหนึ่ง

private void runOrDont()
{
    if (this.battery)
    {
        if (this.battery.working && this.switchedOn)
        {
            this.run();
            return;
        }
    }
    this.dontRun();
}

(เราไม่ทราบว่าการนำไปปฏิบัตินั้นดีหรือไม่ดี) คุณสามารถจินตนาการได้ว่าอาจมีฟังก์ชั่นหลายอย่างที่เกี่ยวข้องกับระบบอิเล็กทรอนิกส์ที่ไม่เกี่ยวข้องกับสิ่งใดใน WheeledVehicle และในทางกลับกัน

ฉันไม่แน่ใจว่าจะตัดสินตัวอย่างนั้นหรือไม่เนื่องจากมีห้องสำหรับการตีความที่นั่น คุณสามารถคิดในแง่ของเครื่องบินที่ขยายยานพาหนะและ FlyingObject และนกที่ขยายสัตว์และ FlyingObject หรือในแง่ของตัวอย่างที่บริสุทธิ์กว่ามาก


24
มันยังสนับสนุนการสืบทอดเหนือองค์ประกอบ ... (เมื่อมันควรจะเป็นวิธีอื่น ๆ รอบ ๆ )
วงล้อประหลาด

34
"คุณสามารถทำให้พลาด" เป็นเหตุผลที่ถูกต้องสมบูรณ์ในการลบคุณลักษณะ การออกแบบภาษาสมัยใหม่ค่อนข้างแตกหักในจุดนั้น แต่โดยทั่วไปคุณสามารถแบ่งภาษาเป็น "พลังจากข้อ จำกัด " และ "พลังจากความยืดหยุ่น" ไม่ว่า "ถูกต้อง" ฉันไม่คิดว่าพวกเขาทั้งคู่มีจุดแข็งที่จะทำ MI เป็นหนึ่งในสิ่งเหล่านั้นที่ใช้สำหรับความชั่วร้ายบ่อยกว่าไม่และภาษาที่ จำกัด จึงลบมันออก ตัวเลือกที่ยืดหยุ่นไม่ได้เพราะ "บ่อยกว่าไม่" ไม่ใช่ "ตัวอักษรเสมอ" ที่กล่าวว่า mixins / ลักษณะเป็นทางออกที่ดีกว่าสำหรับกรณีทั่วไปฉันคิดว่า
Phoshi

8
มีทางเลือกที่ปลอดภัยกว่าสำหรับการสืบทอดหลายรายการในหลายภาษาซึ่งนอกเหนือไปจากส่วนต่อประสาน ลองดู Scala's Traits- พวกมันทำหน้าที่เหมือนอินเทอร์เฟซที่มีการติดตั้งเสริม แต่มีข้อ จำกัด บางอย่างที่ช่วยป้องกันปัญหาเช่นปัญหาเพชรที่เกิดขึ้น
KChaloux

5
ดูคำถามที่ปิดไว้ก่อนหน้านี้: programmers.stackexchange.com/questions/122480/…
Doc Brown

19
KiaSpectraไม่; มันมีเครื่องใช้ไฟฟ้าและอาจจะเป็น(ซึ่งจะขยาย... ) ElectronicElectronicCarCar
ไบรอัน S

คำตอบ:


68

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

อย่างไรก็ตามนกมีลักษณะอื่น ๆ ที่ Pegasi ไม่มี ตัวอย่างเช่นนกวางไข่เพกาซีมีกำเนิดชีวิต หากการสืบทอดเป็นวิธีการเดียวในการส่งผ่านลักษณะการแบ่งปันของคุณคุณจะไม่สามารถแยกคุณลักษณะการวางไข่จากเพกาซัสได้

บางภาษาเลือกที่จะสร้างลักษณะที่ชัดเจนภายในภาษา ส่วนคนอื่น ๆ จะนำทางคุณไปอย่างอ่อนโยนโดยการลบ MI ออกจากภาษา ทั้งสองวิธีฉันไม่สามารถนึกถึงกรณีเดียวที่ฉันคิดว่า "ผู้ชายฉันต้องการให้ MI ทำสิ่งนี้อย่างถูกต้อง"

นอกจากนี้เราจะหารือกันว่ามรดกคืออะไร เมื่อคุณรับช่วงจากคลาสคุณต้องพึ่งพาคลาสนั้น แต่คุณต้องสนับสนุนสัญญาที่คลาสนั้นสนับสนุนทั้งโดยปริยายและชัดเจน

ใช้ตัวอย่างคลาสสิกของการสืบทอดสี่เหลี่ยมจากสี่เหลี่ยมผืนผ้า รูปสี่เหลี่ยมผืนผ้าแสดงคุณสมบัติความยาวและความกว้างรวมถึงวิธี getPerimeter และ getArea สแควร์จะแทนที่ความยาวและความกว้างเพื่อให้เมื่อมีการตั้งค่าอื่น ๆ จะถูกกำหนดให้ตรงกับ getPerimeter และ getArea จะทำงานเหมือนกัน (2 * ความยาว + 2 * ความกว้างสำหรับปริมณฑลและความยาว * ความกว้างสำหรับพื้นที่)

มีกรณีทดสอบเดียวที่แตกถ้าคุณแทนการใช้งานของสแควร์สำหรับสี่เหลี่ยมนี้

var rectangle = new Square();
rectangle.length= 5;
rectangle.width= 6;
Assert.AreEqual(30, rectangle.GetArea()); 
//Square returns 36 because setting the width clobbers the length

มันยากพอที่จะทำให้สิ่งต่าง ๆ ถูกต้องด้วยห่วงโซ่การสืบทอดเดี่ยว มันยิ่งแย่ลงไปอีกเมื่อคุณเพิ่มส่วนผสมลงไป


ข้อผิดพลาดที่ฉันพูดถึงกับ Pegasus ใน MI และความสัมพันธ์แบบสี่เหลี่ยมผืนผ้า / สี่เหลี่ยมนั้นเป็นผลมาจากการออกแบบที่ไม่มีประสบการณ์สำหรับชั้นเรียน โดยทั่วไปการหลีกเลี่ยงการสืบทอดหลาย ๆ แบบเป็นวิธีที่จะช่วยให้นักพัฒนามือใหม่เริ่มหลีกเลี่ยงการยิงตัวเองด้วยการเดินเท้า เช่นเดียวกับหลักการออกแบบทั้งหมดการมีระเบียบวินัยและการฝึกอบรมขึ้นอยู่กับพวกเขาช่วยให้คุณสามารถค้นพบเมื่อมันตกลงที่จะทำลายพวกเขาในเวลา ดูรูปแบบการได้รับทักษะของเดรย์ฟัสที่ระดับผู้เชี่ยวชาญความรู้ที่แท้จริงของคุณจะอยู่เหนือความเชื่อมั่นสูงสุด / หลักการ คุณสามารถ "รู้สึก" เมื่อกฎใช้ไม่ได้

และฉันก็เห็นด้วยว่าฉันค่อนข้างโกงกับตัวอย่าง "โลกแห่งความจริง" ว่าทำไม MI ถึงถูกขมวดคิ้ว

ลองดูกรอบ UI โดยเฉพาะลองมาดูวิดเจ็ตบางตัวที่แปรงในตอนแรกอาจดูเหมือนว่ามันเป็นแค่การรวมกันของสองคน เหมือน ComboBox ComboBox เป็นกล่องข้อความที่มี DropDownList รองรับ คือฉันสามารถพิมพ์ค่าหรือฉันสามารถเลือกจากรายการค่าที่กำหนดไว้ล่วงหน้า วิธีการที่ไร้เดียงสาคือการสืบทอด ComboBox จาก TextBox และ DropDownList

แต่กล่องข้อความของคุณได้รับคุณค่าจากสิ่งที่ผู้ใช้พิมพ์ ในขณะที่ DDL ได้รับค่าจากสิ่งที่ผู้ใช้เลือก ใครเป็นแบบอย่าง? DDL อาจได้รับการออกแบบมาเพื่อตรวจสอบและปฏิเสธอินพุตที่ไม่ได้อยู่ในรายการค่าดั้งเดิม เราจะแทนที่ตรรกะนั้นหรือไม่? นั่นหมายความว่าเราต้องเปิดเผยตรรกะภายในเพื่อให้ผู้สืบทอดแทนที่ หรือแย่กว่านั้นเพิ่มตรรกะลงในคลาสฐานที่มีเพื่อสนับสนุนคลาสย่อย (ละเมิดหลักการการพึ่งพาการพึ่งพา )

การหลีกเลี่ยง MI ช่วยให้คุณก้าวเท้าเลี่ยงอันตรายนี้โดยสิ้นเชิง และอาจทำให้คุณแตกคุณลักษณะทั่วไปที่สามารถใช้ซ้ำได้ของวิดเจ็ต UI ของคุณเพื่อให้สามารถใช้งานได้ตามต้องการ ตัวอย่างที่ยอดเยี่ยมของสิ่งนี้คือWPF Attached Propertyซึ่งอนุญาตให้องค์ประกอบเฟรมเวิร์กใน WPF จัดเตรียมคุณสมบัติที่อิลิเมนต์เฟรมเวิร์กอื่นสามารถใช้ได้โดยไม่ต้องสืบทอดจากอิลิเมนต์พาเรนต์เฟรม

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

นักพัฒนาใช้แนวคิดนี้เพิ่มเติมและใช้คุณสมบัติที่แนบมาเป็นวิธีในการทำพฤติกรรมขององค์ประกอบ (ตัวอย่างนี่คือโพสต์ของฉันในการสร้างGridView ที่เรียงลำดับได้โดยใช้คุณสมบัติที่แนบมาซึ่งเขียนก่อน WPF รวม DataGrid) วิธีการดังกล่าวได้รับการยอมรับว่าเป็นรูปแบบการออกแบบ XAML ที่เรียกว่าพฤติกรรมที่แนบมา

หวังว่านี่จะให้ข้อมูลเชิงลึกมากขึ้นเกี่ยวกับสาเหตุที่ทำให้หลายมรดกสืบทอดโดยทั่วไป


14
"ผู้ชายฉันต้องการ MI เพื่อทำสิ่งนี้อย่างถูกต้อง" - ดูคำตอบของฉันสำหรับ exmaple สรุปแนวคิดของมุมฉากที่ตอบสนองความสัมพันธ์แบบ is-a สมเหตุสมผลสำหรับ MI พวกมันหายากมาก แต่มีอยู่จริง
MrFox

13
ผู้ชายฉันเกลียดตัวอย่างสี่เหลี่ยมจตุรัสนั้น มีตัวอย่างที่ให้แสงสว่างมากกว่านี้มากมายที่ไม่ต้องการความไม่แน่นอน
asmeurer

9
ฉันรักที่คำตอบของ "ให้ตัวอย่างจริง" เพื่อหารือเกี่ยวกับสิ่งมีชีวิตตัวละคร เหน็บแนมที่ยอดเยี่ยม +1
jjathman

3
ดังนั้นคุณกำลังบอกว่าการสืบทอดหลาย ๆ อย่างไม่ดีเนื่องจากการสืบทอดนั้นไม่ดี (ตัวอย่างของคุณเกี่ยวกับการสืบทอดอินเทอร์เฟซเดียว) ดังนั้นตามคำตอบนี้เพียงอย่างเดียวภาษาควรจะกำจัดมรดกและอินเทอร์เฟซหรือมีหลายมรดก
ctrl-alt-delor

12
ฉันไม่คิดว่าตัวอย่างเหล่านี้ใช้งานได้จริง ๆ ... ไม่มีใครบอกว่าเพกาซัสเป็นนกและม้า ... คุณบอกว่ามันเป็น WingedAnimal และ HoofedAnimal ตัวอย่างของรูปสี่เหลี่ยมจัตุรัสและสี่เหลี่ยมผืนผ้ามีเหตุผลเพิ่มเติมเล็กน้อยเนื่องจากคุณพบว่าตัวเองมีวัตถุที่ทำงานแตกต่างไปจากที่คุณคิดตามคำจำกัดความที่ยืนยัน อย่างไรก็ตามฉันคิดว่าสถานการณ์นี้จะเป็นความผิดของโปรแกรมเมอร์ที่ไม่สามารถจับสิ่งนี้ได้ (หากนี่เป็นข้อพิสูจน์ว่าเป็นปัญหา)
richard

60

มีบางอย่างที่ฉันไม่เห็นใช่ไหม

การยอมให้มีการสืบทอดหลายอย่างทำให้กฎเกี่ยวกับฟังก์ชั่นโอเวอร์โหลดและการจัดส่งเสมือนมีความยุ่งยากมากยิ่งขึ้นรวมถึงการใช้ภาษาในการจัดวางวัตถุ สิ่งเหล่านี้ส่งผลกระทบต่อผู้ออกแบบ / ผู้พัฒนาภาษาค่อนข้างน้อยและยกระดับความสูงให้สูงขึ้นเพื่อให้ได้ภาษาที่มีเสถียรภาพ

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

ในกรณีนี้คุณมี 3 สถานการณ์:

  1. A ไม่รู้อะไรเลยเกี่ยวกับ B - เยี่ยมมากคุณสามารถรวมชั้นเรียนเข้าด้วยกันเพราะคุณโชคดี
  2. A รู้เกี่ยวกับ B - ทำไม A ถึงไม่ได้รับมรดกจาก B?
  3. A และ B รู้เกี่ยวกับกันและกัน - ทำไมคุณไม่ทำชั้นเดียว? ประโยชน์ที่ได้รับจากการทำสิ่งเหล่านี้ควบคู่กัน แต่สามารถทดแทนได้บางส่วน?

โดยส่วนตัวแล้วฉันคิดว่าการสืบทอดหลายครั้งมีการแร็พที่ไม่ดีและระบบการจัดวางลักษณะที่ดีของลักษณะนิสัยจะมีประสิทธิภาพ / มีประโยชน์จริง ๆ ... แต่มีหลายวิธีที่จะสามารถนำไปใช้งานได้ไม่ดีและด้วยเหตุผลหลายประการ ไม่ใช่ความคิดที่ดีในภาษาอย่าง C ++

[แก้ไข] เกี่ยวกับตัวอย่างของคุณนั่นไร้สาระ Kia มีอุปกรณ์อิเล็กทรอนิกส์ มันมีเครื่องยนต์ ในทำนองเดียวกันอุปกรณ์อิเล็กทรอนิกส์มีแหล่งพลังงานซึ่งเพิ่งเกิดขึ้นเป็นแบตเตอรี่รถยนต์ การสืบทอดให้มรดกหลายอย่างโดยลำพังไม่มีที่นั่น


8
อีกคำถามที่น่าสนใจคือคลาสพื้นฐาน + คลาสพื้นฐานของพวกเขาได้รับการเตรียมใช้งานอย่างไร การห่อหุ้ม> มรดก
jozefg

"ระบบที่ดีในการจัดแต่งรูปแบบลักษณะจะมีประสิทธิภาพ / มีประโยชน์จริงๆ ... " - ระบบเหล่านี้รู้จักกันในชื่อmixinsและมีประสิทธิภาพ / มีประโยชน์ Mixins สามารถนำมาใช้โดยใช้ MI แต่ไม่จำเป็นต้องมีการสืบทอดหลายรายการสำหรับ mixins บางภาษารองรับมิกซ์อินโดยเนื้อแท้โดยไม่มี MI
BlueRaja - Danny Pflughoeft

สำหรับ Java โดยเฉพาะเรามีอินเตอร์เฟสหลายตัวและ INVOKEINTERFACE ดังนั้น MI จะไม่มีผลกระทบด้านประสิทธิภาพที่ชัดเจน
Daniel Lubarov

Tetastyn ฉันยอมรับว่าตัวอย่างไม่ดีและไม่ตอบคำถามของเขา การรับมรดกไม่ได้มีไว้สำหรับคำคุณศัพท์ (อิเล็กทรอนิกส์) แต่มีไว้สำหรับคำนาม (ยานพาหนะ)
richard

29

เหตุผลเดียวที่ทำไมมันไม่ได้รับอนุญาตเพราะมันทำให้คนยิงตัวเองได้ง่าย

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

หากนักพัฒนาของคุณคุ้นเคยกับ MI และ MI เข้าใจในบริบทของสิ่งที่คุณกำลังทำอยู่คุณจะพลาดอย่างมากในภาษาที่ไม่รองรับ ในเวลาเดียวกันถ้าทีมไม่พอใจกับมันหรือไม่มีความต้องการที่แท้จริงสำหรับมันและผู้คนใช้มันเป็น 'เพราะพวกเขาทำได้' จากนั้นมันก็ตอบโต้ได้

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

แก้ไข

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

สมมติว่าคุณกำลังออกแบบแอปพลิเคชันตลาดทุน คุณต้องการแบบจำลองข้อมูลสำหรับหลักทรัพย์ หลักทรัพย์บางตัวเป็นผลิตภัณฑ์ตราสารทุน (หุ้น, ทรัสต์เพื่อการลงทุนด้านอสังหาริมทรัพย์และอื่น ๆ ) ได้แก่ ตราสารหนี้ (พันธบัตร, หุ้นกู้นิติบุคคล) และตราสารอนุพันธ์อื่น ๆ (ตัวเลือก, สัญญาล่วงหน้า) ดังนั้นหากคุณกำลังหลีกเลี่ยง MI คุณจะต้องทำแผนผังการสืบทอดที่เรียบง่ายและชัดเจนมาก หุ้นจะได้รับทุนส่วนตราสารหนี้จะได้รับหนี้ เยี่ยมมาก แต่แล้วอนุพันธ์คืออะไร? พวกเขาสามารถขึ้นอยู่กับผลิตภัณฑ์ที่เหมือนตราสารทุนหรือผลิตภัณฑ์ที่คล้ายเดบิต? โอเคฉันเดาว่าเราจะทำให้สาขาย่อยของเราได้รับมรดกมากกว่า โปรดทราบว่าตราสารอนุพันธ์บางรายการอ้างอิงจากผลิตภัณฑ์ตราสารทุนตราสารหนี้หรือไม่ใช่ ดังนั้นต้นไม้แห่งการรับมรดกของเราจึงซับซ้อน จากนั้นนักวิเคราะห์ธุรกิจจะมาและบอกคุณว่าตอนนี้เราสนับสนุนหลักทรัพย์ที่จัดทำดัชนีแล้ว (ตัวเลือกดัชนี, ดัชนีทางเลือกในอนาคต) และสิ่งเหล่านี้อาจอิงจากตราสารทุนหนี้หรืออนุพันธ์ นี่กำลังยุ่ง! ตัวเลือกในอนาคตดัชนีของฉันได้รับส่วนของ -> หุ้น -> ตัวเลือก -> ดัชนีหรือไม่ ทำไมไม่เลือก -> หุ้น -> ดัชนี -> ตัวเลือก ถ้าวันหนึ่งฉันพบทั้งคู่ในรหัสของฉัน (เรื่องนี้เกิดขึ้น; เรื่องจริง)

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

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


27
ฉันทำงานด้านการเงิน มีเหตุผลที่โปรแกรมการทำงานเป็นที่ต้องการมากกว่า OO ในซอฟต์แวร์การเงิน และคุณได้ชี้ให้เห็น MI ไม่ได้แก้ไขปัญหานี้มันทำให้รุนแรงขึ้น เชื่อฉันฉันไม่สามารถบอกคุณได้กี่ครั้งที่ฉันเคยได้ยิน "มันเป็นแบบนี้ ... ยกเว้น" เมื่อจัดการกับลูกค้าทางการเงินที่ยกเว้นกลายเป็นความหายนะของการดำรงอยู่ของฉัน
Michael Brown

8
นี้ดูเหมือนว่าแก้ปัญหาได้มากกับอินเตอร์เฟซกับผม ... ในความเป็นจริงมันก็ดูเหมือนดีขึ้นเหมาะกับการเชื่อมต่อมากกว่าสิ่งอื่นใด EquityและDebtใช้งานทั้งสองISecurityอย่าง DerivativeมีISecurityคุณสมบัติ ตัวมันเองอาจจะISecurityเหมาะสมถ้า (ฉันไม่รู้การเงิน) IndexedSecuritiesมีคุณสมบัติของอินเทอร์เฟซอีกครั้งซึ่งจะถูกนำไปใช้กับประเภทที่อนุญาตให้ใช้ หากพวกเขาทั้งหมดISecurityแล้วพวกเขาทั้งหมดมีISecurityคุณสมบัติและสามารถซ้อนกันโดยพล ...
Bobson

11
@ Bobon ปัญหามาในที่ที่คุณต้องใช้อินเทอร์เฟซสำหรับแต่ละตัวดำเนินการและในหลาย ๆ กรณีมันก็เหมือนกัน คุณสามารถใช้การมอบหมาย / องค์ประกอบ แต่จากนั้นคุณสูญเสียการเข้าถึงสมาชิกส่วนตัว และเนื่องจาก Java ไม่มีตัวแทน / lambdas ... จึงไม่มีวิธีที่ดีที่จะทำ ยกเว้นอาจเป็นด้วยเครื่องมือมุมมองเช่น Aspect4J
Michael Brown

10
@ Bobon สิ่งที่ Mike Brown พูด ใช่คุณสามารถออกแบบโซลูชันด้วยส่วนต่อประสาน แต่มันจะเป็น clunky สัญชาตญาณของคุณที่จะใช้อินเทอร์เฟซนั้นถูกต้องมากมันเป็นความปรารถนาที่ซ่อนเร้นสำหรับ mixins / การสืบทอดหลายอย่าง :)
MrFox

11
สิ่งนี้สัมผัสกับสิ่งแปลกประหลาดที่โปรแกรมเมอร์ OO ต้องการที่จะคิดในแง่ของการสืบทอดและมักจะไม่ยอมรับการมอบหมายเป็นวิธีการ OO ที่ถูกต้องซึ่งโดยทั่วไปแล้วจะให้วิธีการแก้ปัญหาที่บางกว่าและบำรุงรักษาได้มากกว่า การทำงานด้านการเงินและการดูแลสุขภาพฉันเห็นว่าระเบียบ MI ที่ไม่สามารถจัดการได้สามารถสร้างขึ้นได้โดยเฉพาะอย่างยิ่งเมื่อกฎหมายภาษีและสุขภาพเปลี่ยนไปทุกปีและคำจำกัดความของวัตถุปีที่ผ่านมาไม่ถูกต้องสำหรับปีนี้ และต้องสามารถใช้แทนกันได้) องค์ประกอบทำให้โค้ดแบบลีนที่ง่ายต่อการทดสอบและค่าใช้จ่ายน้อยลงเมื่อเวลาผ่านไป
Shaun Wilson

11

คำตอบอื่น ๆ ที่นี่ดูเหมือนจะเป็นทฤษฎี ดังนั้นนี่คือตัวอย่างของ Python ที่เป็นรูปธรรมทำให้เรียบง่ายลงซึ่งฉันได้ทุบหัวทิ่มเข้าไปซึ่งต้องใช้การรีแฟคเตอร์จำนวนพอใช้:

class Foo(object):
   def zeta(self):
      print "foozeta"

class Bar(object):
   def zeta(self):
      print "barzeta"

   def barstuff(self):
      print "barstuff"
      self.zeta()

class Bang(Foo, Bar):
   def stuff(self):
      self.zeta()
      print "---"
      self.barstuff()

z = Bang()
z.stuff()

Barถูกเขียนขึ้นโดยสมมติว่ามีการใช้งานของตัวเองzeta()ซึ่งโดยทั่วไปเป็นข้อสันนิษฐานที่ค่อนข้างดี คลาสย่อยควรแทนที่มันตามความเหมาะสมเพื่อที่จะทำสิ่งที่ถูกต้อง น่าเสียดายที่ชื่อเหล่านี้เหมือนกันโดยบังเอิญ - พวกเขาทำสิ่งที่แตกต่างกัน แต่Barตอนนี้กำลังเรียกFooใช้งาน:

foozeta
---
barstuff
foozeta

มันค่อนข้างน่าผิดหวังเมื่อไม่มีข้อผิดพลาดเกิดขึ้นแอปพลิเคชันเริ่มทำงานผิดพลาดเล็กน้อยและการเปลี่ยนรหัสที่ทำให้เกิด (การสร้างBar.zeta) ดูเหมือนจะไม่เกิดปัญหา


ปัญหาไดมอนด์ถูกหลีกเลี่ยงผ่านการโทรถึงได้super()อย่างไร?
Panzercrisis

@Panzercrisis ขออภัยไม่เป็นไรส่วนนั้น ฉัน misremembered ซึ่งปัญหา "ปัญหาเพชร" มักจะหมายถึง - งูใหญ่มีปัญหาที่แยกต่างหากที่เกิดจากการสืบทอดรูปทรงเพชรที่super()ได้รับรอบ
Izkata

5
ฉันดีใจที่ MI ของ C ++ ได้รับการออกแบบมาดีกว่ามาก ข้อผิดพลาดดังกล่าวเป็นไปไม่ได้ คุณต้องทำให้แก้ปัญหาด้วยตนเองก่อนที่จะรวบรวมรหัส
Thomas Eding

2
การเปลี่ยนลำดับของการสืบทอดBangไปBar, Fooยังจะช่วยแก้ไขปัญหาด้วย - แต่ประเด็นของคุณคือสิ่งที่ดี +1
Sean Vieira

1
@ThomasEding Bar.zeta(self)คุณสามารถกระจ่างยังคงด้วยตนเอง:
ไทเลอร์ครอมป์ตัน

9

ฉันจะยืนยันว่าไม่มีปัญหาจริงใด ๆ กับ MI ในภาษาที่เหมาะสม กุญแจสำคัญคือการอนุญาตให้โครงสร้างเพชร แต่ต้องการให้ประเภทย่อยให้แทนที่ของตัวเองแทนการรวบรวมรวบรวมหนึ่งในการใช้งานขึ้นอยู่กับกฎบางอย่าง

ฉันทำสิ่งนี้ในGuavaภาษาที่ฉันใช้งาน คุณลักษณะหนึ่งของ Guava คือเราสามารถเรียกใช้การเรียกใช้วิธีพิเศษแบบ supertype ดังนั้นจึงเป็นเรื่องง่ายที่จะระบุว่าการติดตั้ง supertype ใดที่ควรจะ "สืบทอด" โดยไม่มีไวยากรณ์พิเศษใด ๆ :

type Sequence[+A] {
  String toString() {
    return "[" + ... + "]";
  }
}

type Set[+A] {
  String toString() {
    return "{" + ... + "}";
  }
}

type OrderedSet[+A] extends Sequence[A], Set[A] {
  String toString() {
    // This is Guava's syntax for statically invoking instance methods
    return Set.toString(this);
  }
}

หากเราไม่ได้ให้OrderedSetตัวเองtoStringเราจะได้รับข้อผิดพลาดในการรวบรวม ไม่แปลกใจ.

ฉันพบว่า MI มีประโยชน์อย่างยิ่งกับคอลเล็กชัน ตัวอย่างเช่นฉันต้องการใช้RandomlyEnumerableSequenceประเภทเพื่อหลีกเลี่ยงการประกาศgetEnumeratorสำหรับอาร์เรย์ deques และอื่น ๆ :

type Enumerable[+A] {
  Source[A] getEnumerator();
}

type Sequence[+A] extends Enumerable[A] {
  A get(Int index);
}

type RandomlyEnumerableSequence[+A] extends Sequence[A] {
  Source[A] getEnumerator() {
    ...
  }
}

type DynamicArray[A] extends MutableStack[A],
                             RandomlyEnumerableSequence[A] {
  // No need to define getEnumerator.
}

หากเราไม่มี MI เราสามารถเขียนRandomAccessEnumeratorคอลเล็กชั่นหลายอย่างให้ใช้ แต่ต้องเขียนgetEnumeratorวิธีสั้น ๆ ที่ยังคงเพิ่มแผ่นความร้อน

ในทำนองเดียวกัน, มิชิแกนจะเป็นประโยชน์สำหรับการสืบทอดการใช้งานมาตรฐานequals, hashCodeและtoStringสำหรับคอลเลกชัน


1
@ AndyzSmith ฉันเห็นประเด็นของคุณ แต่ไม่ใช่ความขัดแย้งในการถ่ายทอดทั้งหมดเป็นปัญหาทางความหมายที่แท้จริง พิจารณาตัวอย่างของฉัน - ไม่มีประเภทใดที่ให้คำสัญญาเกี่ยวกับtoStringพฤติกรรมของดังนั้นการลบล้างพฤติกรรมของมันจึงไม่ละเมิดหลักการการทดแทน บางครั้งคุณยังได้รับ "ความขัดแย้ง" ระหว่างวิธีการที่มีพฤติกรรมเหมือนกัน แต่มีอัลกอริทึมที่แตกต่างกันโดยเฉพาะอย่างยิ่งกับคอลเลกชัน
Daniel Lubarov

1
@Asmageddon เห็นด้วยตัวอย่างของฉันเกี่ยวข้องกับการทำงานเท่านั้น คุณเห็นว่าอะไรคือข้อดีของมิกซ์อิน? อย่างน้อยใน Scala ลักษณะเป็นเพียงคลาสนามธรรมซึ่งอนุญาต MI - พวกเขายังสามารถใช้สำหรับตัวตนและยังทำให้เกิดปัญหาเพชร มีภาษาอื่น ๆ ที่มิกซ์อินมีความแตกต่างที่น่าสนใจมากกว่านี้ไหม?
Daniel Lubarov

1
เทคนิคนามธรรมคลาสสามารถใช้เป็นอินเทอร์เฟซสำหรับฉันประโยชน์เพียงความจริงของการสร้างตัวเองเป็น "เคล็ดลับการใช้งาน" ซึ่งหมายความว่าฉันรู้ว่าสิ่งที่คาดหวังเมื่อฉันเห็นรหัส ที่กล่าวว่าฉันกำลังเขียนโปรแกรมใน Lua ซึ่งไม่มีรูปแบบ OOP โดยธรรมชาติ - ระบบคลาสที่มีอยู่ทั่วไปอนุญาตให้รวมถึงตารางเป็น mixins และสิ่งที่ฉันเข้ารหัสใช้คลาสเป็น mixins ความแตกต่างเพียงอย่างเดียวคือคุณสามารถใช้การสืบทอด (เดี่ยว) เพื่อระบุตัวตน, มิกซ์อินสำหรับการทำงาน (โดยไม่มีข้อมูลตัวตน) และอินเทอร์เฟซสำหรับการตรวจสอบเพิ่มเติม บางทีมันอาจจะไม่ใช่วิธีที่ดีกว่า แต่ก็เหมาะสมกับฉัน
Llamageddon

2
ทำไมคุณเรียก? มันเป็นเพียงการเข้าร่วม มันไม่มีจุดสุดยอด ทำไมคุณถึงเรียกว่าเพชร? ฉันถามที่นี่แต่ดูเหมือนคำถามต้องห้าม คุณรู้ได้อย่างไรว่าคุณต้องเรียกโครงสร้างสามเหลี่ยมนี้ว่าเป็นเพชรแทนที่จะเข้าร่วม Ordered extends Set and SequenceDiamondhat
Val

1
@RecognizeEvilasWaste ภาษานั้นมีTopประเภทที่แน่นอนซึ่งประกาศtoStringดังนั้นจึงมีโครงสร้างเพชรจริง ๆ แต่ฉันคิดว่าคุณมีประเด็น - "เข้าร่วม" โดยไม่มีโครงสร้างเพชรสร้างปัญหาที่คล้ายกันและภาษาส่วนใหญ่จัดการทั้งสองกรณีด้วยวิธีเดียวกัน
Daniel Lubarov

7

การสืบทอดนั้นไม่สำคัญหรือไม่สำคัญ หากวัตถุสองชนิดที่แตกต่างกันจะถูกทดแทนนั่นคือสิ่งที่สำคัญแม้ว่าพวกเขาจะไม่ได้รับการเชื่อมโยงโดยการสืบทอด

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

การสืบทอดเป็นเคล็ดลับเพื่อหลีกเลี่ยงการใช้รหัสซ้ำ ๆ หากการสืบทอดช่วยให้คุณทำงานได้และการสืบทอดหลายรายการช่วยให้คุณทำงานได้มากขึ้นเมื่อเทียบกับการสืบทอดเพียงครั้งเดียวนั่นคือเหตุผลทั้งหมดที่จำเป็น

ฉันสงสัยว่าบางภาษาใช้การสืบทอดหลายอย่างไม่ดีนักและสำหรับผู้ปฏิบัติงานของภาษาเหล่านั้นนั่นคือความหมายหลายอย่างของการถ่ายทอดทางพันธุกรรม พูดถึงการรับมรดกหลายรายการไปยังโปรแกรมเมอร์ C ++ และสิ่งที่อยู่ในใจคือสิ่งที่เกี่ยวกับปัญหาเมื่อชั้นเรียนจบลงด้วยสำเนาสองชุดของฐานผ่านเส้นทางการสืบทอดสองเส้นทางที่แตกต่างกันและไม่ว่าจะใช้virtualกับชั้นฐานหรือไม่ และอื่น ๆ

ในหลาย ๆ ภาษาการสืบทอดของคลาสจะถูกแช่งด้วยการสืบทอดของสัญลักษณ์ เมื่อคุณได้คลาส D จากคลาส B คุณไม่เพียง แต่สร้างความสัมพันธ์ประเภท แต่เนื่องจากคลาสเหล่านี้ยังใช้เป็นเนมสเปซศัพท์คุณจึงต้องจัดการกับการนำเข้าสัญลักษณ์จาก B เนมสเปซไปยัง D เนมสเปซนอกเหนือจาก ความหมายของสิ่งที่เกิดขึ้นกับประเภท B และ D ตัวเอง การสืบทอดหลายครั้งทำให้เกิดปัญหาการปะทะกันของสัญลักษณ์ ถ้าเราสืบทอดจากcard_deckและgraphicทั้งสองนั้นมีdrawวิธีการ"มี" มันหมายความว่าอะไรกับdrawวัตถุที่เกิดขึ้น? ระบบวัตถุที่ไม่มีปัญหานี้เป็นระบบ Common Lisp อาจไม่ได้เกิดขึ้นโดยบังเอิญการสืบทอดหลายครั้งจะถูกใช้ในโปรแกรม LISP

มีการนำไปใช้อย่างไม่ถูกต้องไม่ควรเกลียดสิ่งใด (เช่นการสืบทอดหลายรายการ)


2
ตัวอย่างของคุณที่มีความยาว () วิธีการของรายการและคอนเทนเนอร์สตริงไม่ดีเนื่องจากทั้งสองกำลังทำสิ่งที่แตกต่างอย่างสิ้นเชิง วัตถุประสงค์ของการสืบทอดไม่ใช่เพื่อลดการทำซ้ำรหัส ถ้าคุณต้องการลดการทำซ้ำรหัสคุณ refactor ชิ้นส่วนทั่วไปในคลาสใหม่
BЈовић

1
@ BЈовићทั้งสองไม่ได้ทำสิ่งที่แตกต่าง "สมบูรณ์" เลย
Kaz

1
การเปรียบเทียบสตริงและรายการสามารถเห็นการซ้ำซ้อนได้มากมาย การใช้การสืบทอดเพื่อใช้วิธีการทั่วไปเหล่านี้จะผิด
BЈовић

1
@ BЈовићไม่ได้มีการพูดในคำตอบว่าสตริงและรายการควรเชื่อมโยงโดยการรับมรดก ค่อนข้างตรงข้าม พฤติกรรมของความสามารถในการแทนที่รายการหรือสตริงในการlengthดำเนินการมีประโยชน์อย่างเป็นอิสระจากแนวคิดของการสืบทอด (ซึ่งในกรณีนี้ไม่ได้ช่วย: เราไม่น่าจะประสบความสำเร็จโดยพยายามแบ่งปันการดำเนินการระหว่าง สองวิธีของlengthฟังก์ชัน) อย่างไรก็ตามอาจมีการสืบทอดที่เป็นนามธรรม: เช่นทั้งรายการและสตริงเป็นลำดับชนิด (แต่ไม่มีการใช้งาน)
Kaz

2
ยิ่งไปกว่านั้นการสืบทอดเป็นวิธีการเปลี่ยนส่วนให้กับคลาสทั่วไป: คือคลาสฐาน
Kaz

1

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

(ตัวอย่างของฉันอาจไม่ใช่สิ่งที่ดีที่สุด แต่พยายามทำให้มีส่วนสำคัญเกี่ยวกับพื้นที่หน่วยความจำหลายแห่งเพื่อจุดประสงค์เดียวกันมันเป็นสิ่งแรกที่อยู่ในใจของฉัน: P)

DDD ที่สุนัขชั้นเรียนขยายจาก caninus และสัตว์เลี้ยง caninus มีตัวแปรที่ระบุปริมาณอาหารที่ควรกิน (จำนวนเต็ม) ภายใต้ชื่อ dietKg แต่สัตว์เลี้ยงก็มีตัวแปรอื่นสำหรับวัตถุประสงค์นั้นมักจะอยู่ภายใต้เดียวกัน ชื่อ (เว้นแต่คุณจะตั้งชื่อตัวแปรอื่นจากนั้นคุณจะมีรหัสพิเศษซึ่งเป็นปัญหาเริ่มต้นที่คุณต้องการหลีกเลี่ยงเพื่อจัดการและรักษาความสมบูรณ์ของตัวแปร bouth) จากนั้นคุณจะมีพื้นที่หน่วยความจำสองแห่งสำหรับพื้นที่ที่แน่นอน จุดประสงค์เดียวกันเพื่อหลีกเลี่ยงสิ่งนี้คุณจะต้องแก้ไขคอมไพเลอร์ของคุณให้จำชื่อนั้นภายใต้เนมสเปซเดียวกันและเพียงแค่กำหนดพื้นที่หน่วยความจำเดียวให้กับข้อมูลนั้นซึ่งน่าเสียดายที่ต้องใช้เวลารวบรวม

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

เชื่อฉันคนที่ใช้ความคิดนี้ยากมากเกี่ยวกับสิ่งนี้ แต่ฉันดีใจที่คุณถามสิ่งที่คุณต้องการคือสิ่งที่เปลี่ยนกระบวนทัศน์;) และเห็นด้วยกับสิ่งนี้ฉันไม่ได้บอกว่ามันเป็นไปไม่ได้ คอมไพเลอร์ multiphased จะต้องดำเนินการและซับซ้อนจริง ๆ ) ฉันแค่บอกว่ามันยังไม่มีถ้าคุณเริ่มโครงการสำหรับคอมไพเลอร์ของคุณเองที่สามารถทำ "นี้" (มรดกหลาย) โปรดแจ้งให้ฉันทราบฉัน ยินดีที่จะเข้าร่วมทีมของคุณ


1
ผู้รวบรวมอาจคิดว่าตัวแปรที่มีชื่อเดียวกันจากคลาสพาเรนต์ต่างกันปลอดภัยที่จะรวมกันได้อย่างไร คุณรับประกันได้ว่า caninus.diet และ pet.diet นั้นมีจุดประสงค์เดียวกันหรือไม่ คุณจะทำอย่างไรกับฟังก์ชั่น / วิธีการที่มีชื่อเดียวกัน?
Mr.Mindor

ฮ่าฮ่าฮ่าฉันเห็นด้วยกับคุณอย่างเต็มที่ @ Mr.Mindor นั่นคือสิ่งที่ฉันกำลังพูดในคำตอบของฉันวิธีเดียวที่อยู่ในใจของฉันเพื่อเข้าใกล้ aproach นั่นคือต้นแบบการเขียนโปรแกรมแบบตะวันออก แต่ฉันไม่ได้บอกว่ามันเป็นไปไม่ได้ และนั่นคือเหตุผลที่ว่าทำไมฉันถึงบอกว่าต้องมีการสันนิษฐานหลายครั้งในระหว่างการรวบรวมและ "ข้อมูล" พิเศษ / ข้อมูลพิเศษจะต้องเขียนโดยโปรแกรมเมอร์ teh (แต่มันเป็นการเข้ารหัสมากขึ้นซึ่งเป็นปัญหาดั้งเดิม)
Ordiel

-1

ในขณะนี้มันไม่เคยเกิดขึ้นกับฉันเลยว่างานเขียนโปรแกรมบางอย่างนั้นแตกต่างจากงานอื่นอย่างสิ้นเชิง - และช่วยได้มากแค่ไหนถ้าภาษาและรูปแบบที่ใช้ถูกปรับให้เข้ากับพื้นที่ปัญหา

เมื่อคุณทำงานคนเดียวหรือแยกส่วนใหญ่ในรหัสที่คุณเขียนมันเป็นพื้นที่ที่มีปัญหาแตกต่างอย่างสิ้นเชิงจากการสืบทอด codebase จาก 40 คนในอินเดียที่ทำงานกับมันเป็นเวลาหนึ่งปีก่อนที่จะส่งมอบให้คุณโดยไม่ต้องผ่านการช่วยเหลือ

ลองนึกภาพว่าคุณเพิ่งได้รับการว่าจ้างจาก บริษัท ในฝันของคุณจากนั้นรับรหัสฐาน ยิ่งไปกว่านั้นลองนึกภาพว่าที่ปรึกษาได้เรียนรู้เกี่ยวกับการสืบทอด (และหลงเสน่ห์) การสืบทอดและการสืบทอดหลายรายการ ... คุณช่วยนึกภาพสิ่งที่คุณกำลังทำอยู่

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

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

เมื่อคุณทำงานเป็นส่วนหนึ่งของทีมคุณต้องการกำหนดเป้าหมายโค้ดที่ง่ายที่สุดที่ให้คุณไม่มีตรรกะซ้ำซ้อน (นั่นคือสิ่งที่ DRY หมายถึงจริงๆไม่ใช่ว่าคุณไม่ควรพิมพ์มากแค่ว่าคุณไม่ต้องเปลี่ยนรหัสใน 2 สถานที่ที่จะแก้ปัญหา!)

มีวิธีที่ง่ายกว่าในการรับรหัส DRY มากกว่าการสืบทอดหลายอย่างดังนั้นการรวมในภาษาสามารถเปิดคุณเฉพาะปัญหาที่ผู้อื่นแทรกซึ่งอาจไม่อยู่ในระดับความเข้าใจเช่นเดียวกับคุณ แม้เป็นการดึงดูดหากภาษาของคุณไม่สามารถเสนอวิธีที่ง่าย / ซับซ้อนน้อยกว่าในการรักษารหัสของคุณให้แห้ง


ยิ่งไปกว่านั้นลองนึกภาพว่าที่ปรึกษาได้เรียนรู้เกี่ยวกับ - ฉันได้เห็นภาษาที่ไม่ใช่ MI จำนวนมาก (เช่น. net) ที่ที่ปรึกษาบ้าคลั่งด้วย DI-IoC ที่อิงกับอินเตอร์เฟส MI ไม่ได้ทำให้เรื่องแย่ลง แต่อาจทำให้ดีขึ้น
gbjbaanb

@gbjbaanb คุณพูดถูกมันเป็นเรื่องง่ายสำหรับคนที่ไม่ได้ถูกเผาเพื่อทำให้เสียคุณสมบัติ - อันที่จริงแล้วมันเกือบจะเป็นข้อกำหนดสำหรับการเรียนรู้คุณสมบัติที่คุณทำมันยุ่ง ๆ เพื่อดูว่าจะใช้มันอย่างไรให้ดีที่สุด ฉันอยากรู้อยากเห็นเพราะคุณดูเหมือนจะบอกว่าไม่มี MI บังคับมากกว่า DI / IoC ที่ฉันไม่ได้เห็น - ฉันจะไม่คิดว่ารหัสที่ปรึกษาที่คุณได้รับกับทุกอินเตอร์เฟซเส็งเคร็ง / DI ดีกว่าที่จะมีต้นไม้ที่สืบทอดกันมาหลายต่อหลายอย่างบ้าคลั่ง คุณมีหน้าที่ฉันสามารถอ่านแทน DI / IoC ด้วย MI ได้หรือไม่?
Bill K

-3

ข้อโต้แย้งที่ใหญ่ที่สุดต่อมรดกหลายประการคือความสามารถที่มีประโยชน์บางอย่างสามารถจัดหาได้และสัจพจน์ที่เป็นประโยชน์บางอย่างสามารถเก็บไว้ในกรอบซึ่ง จำกัด ไว้อย่างรุนแรง (*) แต่ไม่สามารถจัดหาและ / หรือระงับได้โดยไม่มีข้อ จำกัด ดังกล่าว ในหมู่พวกเขา:

  • ความสามารถในการมีหลายโมดูลที่คอมไพล์แยกต่างหากรวมคลาสที่สืบทอดจากคลาสของโมดูลอื่นและคอมไพล์โมดูลที่มีคลาสฐานโดยไม่ต้องคอมไพล์ทุกโมดูลที่สืบทอดจากคลาสฐานนั้น

  • ความสามารถสำหรับชนิดในการสืบทอดการใช้งานสมาชิกจากคลาสพาเรนต์โดยไม่ต้องมีชนิดที่ได้รับมาเพื่อนำมาใช้ใหม่

  • สัจพจน์ที่อินสแตนซ์ของวัตถุใด ๆ อาจเป็น upcast หรือ downcast โดยตรงกับตัวเองหรือประเภทฐานใด ๆ ของมันและ upcasts และ downcasts ดังกล่าวในการรวมกันหรือลำดับใด ๆ มักจะรักษาตัวตน

  • ความจริงที่ว่าถ้าคลาสที่ได้รับมาแทนที่และโยงไปยังสมาชิกระดับฐานสมาชิกฐานจะถูกเรียกโดยตรงโดยรหัสการผูกมัดและจะไม่ถูกเรียกใช้ที่อื่น

(*) โดยทั่วไปแล้วโดยกำหนดให้ประเภทที่สนับสนุนการสืบทอดหลายรายการถูกประกาศเป็น "ส่วนต่อประสาน" แทนคลาสและไม่อนุญาตให้ส่วนต่อประสานทำทุกอย่างที่ชั้นเรียนปกติสามารถทำได้

ถ้าใครอยากจะอนุญาตให้มีการสืบทอดหลายมรดกโดยทั่วไปสิ่งอื่นจะต้องหลีกทาง ถ้า X และ Y ทั้งคู่สืบทอดจาก B ทั้งคู่จะแทนที่สมาชิก M และ chain เดียวกันกับการใช้งานพื้นฐานและหาก D สืบทอดจาก X และ Y แต่ไม่ได้แทนที่ M แล้วให้ยกตัวอย่าง q ประเภท D สิ่งที่ควร (( B) q) .M () ทำอย่างไร การไม่อนุญาตนักแสดงดังกล่าวจะละเมิดความจริงที่บอกว่าวัตถุใด ๆ ที่สามารถถ่ายทอดไปยังประเภทฐานใด ๆ ได้ แต่พฤติกรรมที่เป็นไปได้ของนักแสดงและการเรียกสมาชิกจะละเมิดความจริงเกี่ยวกับวิธีการผูกมัด เราอาจต้องการให้คลาสนั้นโหลดร่วมกับเวอร์ชันพื้นฐานของคลาสพื้นฐานที่คอมไพล์แล้วเท่านั้น แต่มักจะไม่สะดวก การมีรันไทม์ปฏิเสธที่จะโหลดประเภทใด ๆ ที่บรรพบุรุษสามารถเข้าถึงได้โดยมากกว่าหนึ่งเส้นทางอาจใช้การได้ แต่จะ จำกัด ประโยชน์ของการสืบทอดหลายอย่างมาก การอนุญาตเส้นทางที่ใช้ร่วมกันเฉพาะเมื่อไม่มีความขัดแย้งจะสร้างสถานการณ์ที่ X รุ่นเก่าจะเข้ากันได้กับ Y เก่าหรือใหม่และ Y เก่าจะเข้ากันได้กับ X เก่าหรือใหม่ แต่ X ใหม่และ Y ใหม่จะ สามารถทำงานร่วมกันได้แม้ว่าจะไม่ได้ทำสิ่งใดก็ตามที่คาดหวังว่าจะเป็นการเปลี่ยนแปลงที่ผิดพลาด

ภาษาและกรอบการทำงานบางอย่างอนุญาตให้มีการสืบทอดหลายครั้งโดยทฤษฏีว่าสิ่งที่ได้รับจาก MI มีความสำคัญมากกว่าสิ่งที่ต้องยอมแพ้เพื่ออนุญาต อย่างไรก็ตามค่าใช้จ่ายของ MI มีความสำคัญและสำหรับหลาย ๆ กรณีอินเทอร์เฟซจะให้ประโยชน์ 90% ของ MI ด้วยค่าใช้จ่ายเพียงเล็กน้อย


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

2
ปัญหาเกี่ยวกับ MI นั้นสามารถแก้ไขหรือหลีกเลี่ยงได้ทั้งหมดโดยใช้เทคนิคเดียวกับที่คุณใช้กับการสืบทอดเพียงครั้งเดียว ไม่มีความสามารถ / สัจพจน์ที่คุณพูดถึงจะถูกขัดขวางโดย MI ยกเว้นสำหรับวินาทีที่สอง ... และถ้าคุณสืบทอดจากสองคลาสที่มีพฤติกรรมที่แตกต่างกันสำหรับวิธีการเดียวกันคุณก็สามารถแก้ไขความคลุมเครือนั้นได้ แต่ถ้าคุณพบว่าตัวเองต้องทำเช่นนั้นคุณอาจได้รับมรดกในทางที่ผิดแล้ว
cHao

@cHao: หากต้องการเรียนที่ได้รับจะต้องแทนที่วิธีการหลักและไม่เชื่อมโยงกับพวกเขา (กฎเดียวกับอินเทอร์เฟซ) อย่างใดอย่างหนึ่งสามารถหลีกเลี่ยงปัญหา แต่นั่นเป็นข้อ จำกัด ที่ค่อนข้างรุนแรง หากใช้รูปแบบที่การFirstDerivedFooแทนที่ของBarไม่ทำอะไรเลยยกเว้นการเชื่อมโยงไปยังการป้องกันที่ไม่ใช่เสมือนFirstDerivedFoo_BarและการSecondDerivedFooแทนที่การเชื่อมโยงของการป้องกันไม่ใช่เสมือนSecondDerivedBarและหากรหัสที่ต้องการเข้าถึงวิธีฐานใช้วิธีการที่ไม่ใช่เสมือนที่ได้รับการป้องกันเหล่านั้น อาจจะเป็นวิธีการที่สามารถทำงานได้ ...
SuperCat

... (ละทิ้งไวยากรณ์base.VirtualMethod) แต่ฉันไม่รู้การสนับสนุนด้านภาษาใด ๆ ฉันหวังว่าจะมีวิธีที่สะอาดในการแนบโค้ดหนึ่งบล็อกเข้ากับวิธีการป้องกันที่ไม่ใช่เสมือนจริงและวิธีเสมือนที่มีลายเซ็นเดียวกันโดยไม่ต้องใช้วิธีเสมือน "หนึ่งซับ" (ซึ่งจบลงด้วยการใช้มากกว่าหนึ่งบรรทัด ในซอร์สโค้ด)
supercat

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