"ชอบเรียงความมากกว่ามรดก" เป็นเพียงการแก้ปัญหาที่ดี
คุณควรพิจารณาบริบทเนื่องจากนี่ไม่ใช่กฎสากล อย่าใช้มันเพื่อหมายถึงไม่เคยใช้มรดกเมื่อคุณสามารถจัดองค์ประกอบ หากเป็นเช่นนั้นคุณจะแก้ไขได้โดยการห้ามการรับมรดก
ฉันหวังว่าจะทำให้ประเด็นนี้ชัดเจนในโพสต์นี้
ฉันจะไม่พยายามปกป้องข้อดีของการแต่งเพลงด้วยตัวเอง ซึ่งฉันพิจารณาหัวข้อ แต่ฉันจะพูดถึงบางสถานการณ์เมื่อผู้พัฒนาอาจพิจารณาการสืบทอดที่จะดีกว่าโดยใช้การแต่งเพลง ในบันทึกย่อนั้นการสืบทอดมีข้อดีของตัวเองซึ่งฉันพิจารณาหัวข้อด้วย
ตัวอย่างยานพาหนะ
ฉันกำลังเขียนเกี่ยวกับนักพัฒนาที่พยายามทำสิ่งที่โง่เง่าเพื่อจุดประสงค์ในการเล่าเรื่อง
ขอให้เราไปแตกต่างจากตัวอย่างคลาสสิกที่บางหลักสูตร OOP ใช้ ... เรามีVehicle
ระดับแล้วเราได้รับมาCar
, Airplane
, และBalloon
Ship
หมายเหตุ : หากคุณต้องการแสดงตัวอย่างนี้ให้ถือว่าสิ่งเหล่านี้เป็นวัตถุประเภทหนึ่งในวิดีโอเกม
จากนั้นCar
และAirplane
อาจมีรหัสทั่วไปบางอย่างเพราะทั้งคู่สามารถหมุนล้อบนพื้นดินได้ ผู้พัฒนาอาจพิจารณาสร้างคลาสตัวกลางในห่วงโซ่การสืบทอดสำหรับสิ่งนั้น แต่รหัสจริงนอกจากนี้ยังมีการใช้ร่วมกันบางอย่างระหว่างและAirplane
Balloon
พวกเขาสามารถพิจารณาสร้างคลาสตัวกลางอื่นในห่วงโซ่การสืบทอดสำหรับสิ่งนั้น
ดังนั้นผู้พัฒนาจะค้นหามรดกหลายรายการ ณ จุดที่นักพัฒนากำลังมองหาการสืบทอดหลายแบบการออกแบบผิดพลาดไปแล้ว
เป็นการดีกว่าหากวางโมเดลพฤติกรรมนี้เป็นอินเทอร์เฟซและการจัดองค์ประกอบดังนั้นเราจึงสามารถนำกลับมาใช้ใหม่ได้โดยไม่ต้องใช้การสืบทอดหลายคลาส ตัวอย่างเช่นหากนักพัฒนาสร้างFlyingVehicule
คลาส พวกเขาจะบอกว่านั่นAirplane
คือFlyingVehicule
(การสืบทอดคลาส) แต่เราสามารถพูดAirplane
ได้ว่ามีFlying
องค์ประกอบ (องค์ประกอบ) และAirplane
เป็นIFlyingVehicule
(การสืบทอดส่วนต่อประสาน)
ใช้อินเทอร์เฟซถ้าจำเป็นเราสามารถมีหลายมรดก (ของอินเทอร์เฟซ) นอกจากนี้คุณไม่ได้มีเพศสัมพันธ์กับการใช้งานเฉพาะ เพิ่มความสามารถในการนำกลับมาใช้ใหม่และความสามารถในการทดสอบโค้ดของคุณ
โปรดจำไว้ว่าการสืบทอดเป็นเครื่องมือสำหรับ polymorphism นอกจากนี้ความหลากหลายก็เป็นเครื่องมือสำหรับการนำกลับมาใช้ใหม่ หากคุณสามารถเพิ่มความสามารถในการนำรหัสกลับมาใช้ใหม่ได้โดยใช้การจัดองค์ประกอบแล้วให้ทำเช่นนั้น หากคุณไม่แน่ใจว่าองค์ประกอบใดหรือไม่ให้ความสามารถในการนำกลับมาใช้ใหม่ได้ดีกว่า
Amphibious
ทุกสิ่งที่ไม่ต้องกล่าวถึง
ในความเป็นจริงเราอาจไม่ต้องการสิ่งที่ลงไปจากพื้น สตีเฟ่น Hurn มีตัวอย่างฝีปากอื่น ๆ ในบทความของเขา“โปรดปรานองค์ประกอบกว่ามรดก” ส่วน 1และส่วนที่ 2
ความสามารถในการทดแทนและการห่อหุ้ม
ควรA
สืบทอดหรือเรียบเรียงB
?
หากA
เป็นความเชี่ยวชาญเฉพาะด้านB
ที่ควรทำตามหลักการทดแทนของ Liskov การสืบทอดจะเป็นไปได้แม้เป็นที่ต้องการ หากมีสถานการณ์ที่A
ไม่มีการทดแทนที่ถูกต้องB
เราก็ไม่ควรใช้การสืบทอด
เราอาจจะสนใจในการประกอบเป็นรูปแบบของการเขียนโปรแกรมการป้องกันที่จะปกป้องชั้นเรียนมา โดยเฉพาะอย่างยิ่งเมื่อคุณเริ่มใช้B
เพื่อวัตถุประสงค์อื่นที่แตกต่างกันอาจมีแรงกดดันให้เปลี่ยนหรือขยายให้เหมาะสมกับวัตถุประสงค์เหล่านั้นมากขึ้น หากมีความเสี่ยงที่B
อาจเปิดเผยวิธีการที่อาจส่งผลให้สถานะที่ไม่ถูกต้องในA
เราควรจะใช้องค์ประกอบแทนการสืบทอด แม้ว่าเราจะเป็นผู้เขียนของทั้งสองB
และก็เป็นอีกสิ่งหนึ่งที่ไม่ต้องกังวลเกี่ยวกับการจึงสามารถนำมาใช้ลดองค์ประกอบของA
B
เราอาจโต้แย้งว่าหากมีคุณสมบัติB
ที่A
ไม่ต้องการ (และเราไม่ทราบว่าคุณลักษณะเหล่านั้นอาจส่งผลให้สถานะไม่ถูกต้องไม่A
ว่าจะเป็นในการนำไปใช้ในปัจจุบันหรือในอนาคต) เป็นความคิดที่ดีที่จะใช้การจัดองค์ประกอบ แทนการสืบทอด
องค์ประกอบยังมีข้อดีของการอนุญาตให้ใช้งานสวิตช์และการเยาะเย้ยทำให้สบาย
หมายเหตุ : มีสถานการณ์ที่เราต้องการใช้การแต่งเพลงแม้จะมีการทดแทนที่ถูกต้อง เราเก็บถาวรความสามารถในการแทนที่นั้นโดยใช้อินเทอร์เฟซหรือคลาสนามธรรม (ซึ่งจะใช้เมื่อเป็นหัวข้ออื่น) จากนั้นใช้องค์ประกอบที่มีการฉีดพึ่งพาของการใช้งานจริง
ในที่สุดแน่นอนมีข้อโต้แย้งว่าเราควรใช้องค์ประกอบเพื่อปกป้องชั้นผู้ปกครองเพราะมรดกแบ่ง encapsulation ของชั้นผู้ปกครอง:
การสืบทอดทำให้คลาสย่อยมีรายละเอียดเกี่ยวกับการนำไปปฏิบัติของผู้ปกครองมันมักจะกล่าวว่า
- รูปแบบการออกแบบ: องค์ประกอบของซอฟต์แวร์เชิงวัตถุที่ใช้ซ้ำได้ Gang of Four
นั่นคือคลาสผู้ปกครองที่ออกแบบมาไม่ดี นี่คือเหตุผลที่คุณควร:
ออกแบบสำหรับการสืบทอดหรือห้าม
- Java ที่มีประสิทธิภาพ, Josh Bloch
ปัญหาเรื่องโยโย่
กรณีที่องค์ประกอบช่วยก็คือYo-Yo ปัญหา นี่คือคำพูดจาก Wikipedia:
ในการพัฒนาซอฟต์แวร์ปัญหา yo-yo เป็นรูปแบบการต่อต้านที่เกิดขึ้นเมื่อโปรแกรมเมอร์ต้องอ่านและทำความเข้าใจกับโปรแกรมที่กราฟกราฟการสืบทอดมีความยาวและซับซ้อนจนโปรแกรมเมอร์ต้องพลิกระหว่างคำจำกัดความของคลาสที่แตกต่างกันเพื่อที่จะติดตาม การควบคุมการไหลของโปรแกรม
คุณสามารถแก้ไขตัวอย่างเช่น: ระดับของคุณจะไม่ได้รับมรดกจากชั้นเรียนC
B
ชั้นเรียนของคุณC
จะมีสมาชิกประเภทหนึ่งA
ซึ่งอาจเป็นหรือไม่เป็นประเภทวัตถุB
ก็ได้ วิธีนี้คุณจะไม่ได้เขียนโปรแกรมกับรายละเอียดการใช้งานB
แต่เทียบกับสัญญาที่ส่วนต่อประสาน (จาก) A
เสนอ
ตัวอย่างเคาน์เตอร์
กรอบการทำงานจำนวนมากสนับสนุนการสืบทอดมากกว่าองค์ประกอบ (ซึ่งตรงกันข้ามกับสิ่งที่เราโต้เถียง) นักพัฒนาอาจทำเช่นนี้เพราะพวกเขาใส่งานจำนวนมากลงในคลาสพื้นฐานที่นำไปใช้กับการแต่งเพลงจะเพิ่มขนาดของรหัสลูกค้า บางครั้งสิ่งนี้เกิดจากข้อ จำกัด ของภาษา
ตัวอย่างเช่นกรอบงาน PHP ORM อาจสร้างคลาสพื้นฐานที่ใช้วิธีการเวทย์มนตร์เพื่อให้สามารถเขียนโค้ดราวกับว่าวัตถุนั้นมีคุณสมบัติจริง แทนที่จะเป็นรหัสที่จัดการโดยวิธีเวทย์มนตร์จะไปที่ฐานข้อมูลให้ค้นหาเขตข้อมูลที่ร้องขอโดยเฉพาะ (อาจแคชเพื่อขอในอนาคต) และส่งคืน การเขียนด้วยองค์ประกอบจะต้องให้ไคลเอนต์สร้างคุณสมบัติสำหรับแต่ละฟิลด์หรือเขียนโค้ดของเมธอดเวทมนต์บางเวอร์ชัน
ภาคผนวก : มีวิธีอื่น ๆ ที่สามารถอนุญาตให้ขยายวัตถุ ORM ได้ ดังนั้นฉันไม่คิดว่าการสืบทอดเป็นสิ่งจำเป็นในกรณีนี้ มันถูกกว่า.
อีกตัวอย่างหนึ่งเอ็นจิ้นวิดีโอเกมอาจสร้างคลาสพื้นฐานที่ใช้รหัสเนทีฟทั้งนี้ขึ้นอยู่กับแพลตฟอร์มเป้าหมายเพื่อทำการเรนเดอร์ 3D และการจัดการเหตุการณ์ รหัสนี้มีความซับซ้อนและเฉพาะแพลตฟอร์ม มันจะมีราคาแพงและเกิดข้อผิดพลาดได้ง่ายสำหรับผู้ใช้นักพัฒนาของเครื่องมือในการจัดการกับรหัสนี้อันที่จริงนั่นเป็นส่วนหนึ่งของเหตุผลในการใช้โปรแกรม
นอกจากนี้หากไม่มีส่วนการเรนเดอร์ 3D นี่คือจำนวนกรอบงานวิดเจ็ตที่ทำงาน สิ่งนี้ทำให้คุณไม่ต้องกังวลเกี่ยวกับการจัดการข้อความ OS …อันที่จริงในหลาย ๆ ภาษาคุณไม่สามารถเขียนรหัสดังกล่าวได้โดยไม่ต้องมีการซ่อนพื้นเมือง ยิ่งกว่านั้นถ้าคุณต้องทำมันจะทำให้การพกพาของคุณแน่น แต่ด้วยการสืบทอดให้นักพัฒนาไม่ทำลายความเข้ากันได้ (มากเกินไป); คุณจะสามารถย้ายรหัสของคุณไปยังแพลตฟอร์มใหม่ใด ๆ ที่พวกเขาสนับสนุนในอนาคตได้อย่างง่ายดาย
นอกจากนี้ให้พิจารณาว่าหลาย ๆ ครั้งที่เราต้องการแทนที่เพียงไม่กี่วิธีและปล่อยให้ทุกอย่างอื่นมีการใช้งานเริ่มต้น ถ้าเราใช้การจัดเรียงเราจะต้องสร้างวิธีการเหล่านั้นทั้งหมดแม้ว่าจะมอบให้กับวัตถุที่ถูกห่อ
โดยอาร์กิวเมนต์นี้มีจุดที่การเรียงความสามารถเลวร้ายที่สุดสำหรับการบำรุงรักษากว่าการสืบทอด (เมื่อคลาสพื้นฐานนั้นซับซ้อนเกินไป) ถึงกระนั้นโปรดจำไว้ว่าการบำรุงรักษาของการสืบทอดอาจเลวร้ายยิ่งกว่าองค์ประกอบ (เมื่อต้นไม้มรดกซับซ้อนเกินไป) ซึ่งเป็นสิ่งที่ฉันอ้างถึงในปัญหาโยโย่
ในตัวอย่างที่นำเสนอนักพัฒนาไม่ค่อยตั้งใจจะใช้รหัสที่สร้างขึ้นใหม่ผ่านการสืบทอดในโครงการอื่น ๆ ที่ลดความสามารถในการใช้การสืบทอดให้น้อยลงแทนที่จะเป็นองค์ประกอบ นอกจากนี้ด้วยการใช้การสืบทอดผู้พัฒนาเฟรมเวิร์กสามารถให้โค้ดที่ใช้งานง่ายและค้นพบโค้ดได้ง่าย
ความคิดสุดท้าย
อย่างที่คุณเห็นการจัดองค์ประกอบมีความได้เปรียบเหนือการสืบทอดในบางสถานการณ์ไม่ใช่ทุกครั้ง มันเป็นสิ่งสำคัญที่จะต้องพิจารณาบริบทและปัจจัยต่าง ๆ ที่เกี่ยวข้อง (เช่นสามารถนำกลับมาใช้ใหม่ได้การบำรุงรักษาการทดสอบ ฯลฯ ) เพื่อทำการตัดสินใจ กลับไปที่จุดแรก: "ชอบองค์ประกอบมรดก" เป็นเพียงการแก้ปัญหาที่ดี
คุณอาจสังเกตเห็นว่าสถานการณ์หลายอย่างที่ฉันอธิบายสามารถแก้ไขได้ด้วยลักษณะหรือมิกซ์อินในระดับหนึ่ง น่าเศร้าที่สิ่งเหล่านี้ไม่ใช่คุณสมบัติทั่วไปในรายการภาษาที่ยอดเยี่ยมและพวกเขามักจะมาพร้อมกับค่าใช้จ่ายด้านประสิทธิภาพ โชคดีที่วิธีการขยายญาติที่เป็นที่นิยมของพวกเขาและการใช้งานเริ่มต้นบรรเทาสถานการณ์บางอย่าง
ฉันมีโพสต์ล่าสุดที่ฉันพูดคุยเกี่ยวกับบางส่วนของข้อดีของอินเตอร์เฟซในทำไมเราจำเป็นต้องมีการเชื่อมต่อระหว่าง UI ธุรกิจและการเข้าถึงข้อมูลใน C # มันช่วยลดการแยกตัวและลดการใช้ซ้ำและลดความสามารถในการทดสอบคุณอาจสนใจ