เป็นแนวคิดที่ดีที่จะใช้การสืบทอดหลาย ๆ แบบหรือฉันสามารถทำสิ่งอื่นแทนได้หรือไม่?
เป็นแนวคิดที่ดีที่จะใช้การสืบทอดหลาย ๆ แบบหรือฉันสามารถทำสิ่งอื่นแทนได้หรือไม่?
คำตอบ:
มีหลายมรดก (ตัวย่อเป็น MI) มีกลิ่นซึ่งหมายความว่าโดยปกติแล้วมันถูกทำขึ้นด้วยเหตุผลที่ไม่ดีและมันจะพัดกลับไปที่ใบหน้าของผู้ดูแล
สิ่งนี้เป็นจริงสำหรับการสืบทอดดังนั้นมันจึงเป็นความจริงสำหรับการสืบทอดหลาย ๆ อย่าง
วัตถุของคุณจำเป็นต้องสืบทอดจากสิ่งอื่นจริง ๆ หรือไม่? Car
ไม่จำเป็นต้องสืบทอดจากการทำงานหรือจากEngine
มีและสี่Wheel
Car
Engine
Wheel
หากคุณใช้การสืบทอดหลายแบบเพื่อแก้ไขปัญหาเหล่านี้แทนการแต่งเพลงแสดงว่าคุณทำอะไรผิดไป
โดยปกติแล้วคุณได้เรียนA
แล้วB
และทั้งสืบทอดมาจากC
A
และ (อย่าถามฉันว่าทำไม) ใครบางคนตัดสินใจว่าD
จะต้องสืบทอดทั้งจากB
และC
และ
ฉันพบปัญหาประเภทนี้สองครั้งในรอบแปดปีและมันสนุกที่ได้เห็นเพราะ:
D
ไม่ควรสืบทอดมาจากทั้งคู่B
และC
) เพราะนี่เป็นสถาปัตยกรรมที่ไม่ดี (อันที่จริงแล้วC
ไม่ควรมีอยู่เลย ... )A
จะปรากฏสองครั้งในคลาสหลานD
ดังนั้นการอัปเดตฟิลด์ผู้ปกครองหนึ่งรายการจึงA::field
หมายถึงการอัปเดตสองครั้ง (ผ่านB::field
และC::field
) หรือมีบางสิ่งผิดปกติอย่างเงียบ ๆ และพังภายหลัง (ใหม่ตัวชี้ในB::field
และลบC::field
... )การใช้คำหลักเสมือนจริงใน C ++ เพื่อให้มีคุณสมบัติการสืบทอดหลีกเลี่ยงเค้าโครงที่อธิบายไว้ข้างต้นหากนี่ไม่ใช่สิ่งที่คุณต้องการ แต่อย่างไรก็ตามจากประสบการณ์ของฉันคุณอาจทำสิ่งผิดปกติ ...
ในลำดับชั้นวัตถุคุณควรพยายามรักษาลำดับชั้นเป็นแบบทรี (โหนดมีผู้ปกครองหนึ่งคน) ไม่ใช่เป็นกราฟ
ปัญหาที่แท้จริงกับ Diamond of Dread ใน C ++ ( สมมติว่าการออกแบบนั้นเป็นเสียง - ให้คุณตรวจสอบโค้ดของคุณ! ) คือคุณต้องทำการเลือก :
A
จะมีอยู่สองครั้งในเค้าโครงของคุณและมันหมายถึงอะไร? ถ้าใช่แล้วโดยทั้งหมดหมายความว่าสืบทอดมาจากมันสองครั้งตัวเลือกนี้มีสาเหตุมาจากปัญหาและใน C ++ ซึ่งแตกต่างจากภาษาอื่น ๆ คุณสามารถทำได้โดยไม่ต้องใช้ความเชื่อในการออกแบบในระดับภาษา
แต่ก็เหมือนกับพลังทั้งหมดด้วยพลังนั้นมาพร้อมความรับผิดชอบ: ให้การออกแบบของคุณได้รับการตรวจสอบ
การสืบทอดหลาย ๆ ครั้งของคลาสที่เป็นศูนย์หรือหนึ่งคลาสและอินเทอร์เฟซที่เป็นศูนย์หรือมากกว่านั้นก็โอเคเพราะคุณจะไม่พบกับ Diamond of Dread ที่อธิบายไว้ข้างต้น ในความเป็นจริงนี่คือสิ่งที่ทำใน Java
โดยปกติแล้วสิ่งที่คุณหมายถึงเมื่อ C สืบทอดจากA
และB
เป็นที่ผู้ใช้สามารถใช้C
เป็นถ้ามันเป็นA
และ / B
หรือราวกับว่ามันเป็น
ใน C ++ อินเตอร์เฟสคือคลาสนามธรรมซึ่งมี:
การรับมรดกหลายรายการของวัตถุจริงหนึ่งศูนย์และอินเทอร์เฟซศูนย์หรือมากกว่านั้นไม่ถือว่าเป็น "เหม็น" (อย่างน้อยก็ไม่มาก)
ประการแรกรูปแบบ NVI สามารถใช้สร้างอินเทอร์เฟซได้เนื่องจากเกณฑ์จริงคือไม่มีสถานะ (เช่นไม่มีตัวแปรสมาชิกยกเว้นthis
) จุดอินเทอร์เฟซแบบนามธรรมของคุณคือการเผยแพร่สัญญา ("คุณสามารถโทรหาฉันด้วยวิธีนี้และวิธีนี้") ไม่มากไปกว่านี้ ข้อ จำกัด ของการมีวิธีเสมือนนามธรรมที่เป็นนามธรรมควรเป็นตัวเลือกการออกแบบไม่ใช่ข้อผูกมัด
ประการที่สองใน C ++ มันสมเหตุสมผลที่จะสืบทอดจากอินเตอร์เฟสนามธรรม (แม้จะมีค่าใช้จ่ายเพิ่มเติม / โดยอ้อม) หากคุณไม่มีและมรดกอินเทอร์เฟซปรากฏขึ้นหลายครั้งในลำดับชั้นของคุณคุณจะมีความคลุมเครือ
ประการที่สามการวางแนววัตถุนั้นยอดเยี่ยม แต่ไม่ใช่ความจริงเพียงอย่างเดียวนั่นคือTMใน C ++ ใช้เครื่องมือที่เหมาะสมและจำไว้เสมอว่าคุณมีกระบวนทัศน์อื่นใน C ++ ที่นำเสนอโซลูชั่นที่แตกต่าง
บางครั้งใช่.
โดยปกติแล้วคุณC
เรียนสืบทอดจากA
และB
และA
และB
มีวัตถุทั้งสองไม่เกี่ยวข้องกัน (คือไม่ได้อยู่ในลำดับชั้นเดียวกันอะไรกันแนวคิดที่แตกต่างกัน ฯลฯ )
ตัวอย่างเช่นคุณอาจมีระบบที่Nodes
มีพิกัด X, Y, Z สามารถทำการคำนวณทางเรขาคณิตจำนวนมาก (อาจเป็นจุดส่วนหนึ่งของวัตถุทางเรขาคณิต) และแต่ละโหนดเป็นตัวแทนอัตโนมัติที่สามารถสื่อสารกับตัวแทนอื่น ๆ
บางทีคุณมีสิทธิ์เข้าถึงไลบรารีสองแห่งแล้วแต่ละแห่งมีเนมสเปซของตัวเอง (อีกเหตุผลที่ใช้เนมสเปซ ... แต่คุณใช้เนมสเปซไม่ใช่หรือ?) อีกอันหนึ่งgeo
กับอีกอันai
เพื่อให้คุณมีของคุณเองown::Node
การสืบทอดมาทั้งจากและai::Agent
geo::Point
นี่คือช่วงเวลาที่คุณควรถามตัวเองว่าคุณไม่ควรใช้การแต่งเพลงแทนหรือไม่ ถ้าown::Node
เป็นจริงทั้ง a ai::Agent
และ a จริงๆgeo::Point
แล้วการจัดองค์ประกอบจะไม่ทำ
จากนั้นคุณจะต้องมีการสืบทอดหลายครั้งโดยให้คุณown::Node
สื่อสารกับตัวแทนอื่น ๆ ตามตำแหน่งในพื้นที่ 3 มิติ
(คุณจะสังเกตได้ว่าai::Agent
และgeo::Point
ไม่เกี่ยวข้องทั้งหมดโดยสิ้นเชิง ... สิ่งนี้จะช่วยลดอันตรายจากการสืบทอดหลายอย่างมาก)
มีกรณีอื่น ๆ :
this
)บางครั้งคุณสามารถใช้การจัดองค์ประกอบและบางครั้ง MI ก็ดีกว่า ประเด็นคือ: คุณมีทางเลือก ทำด้วยความรับผิดชอบ (และตรวจสอบรหัสของคุณ)
ส่วนใหญ่แล้วในประสบการณ์ของฉันไม่มี MI ไม่ใช่เครื่องมือที่ถูกต้องแม้ว่าจะดูเหมือนว่าใช้งานได้เพราะขี้เกียจสามารถใช้งานร่วมกันได้โดยไม่ต้องคำนึงถึงผลที่จะตามมา (เช่นทำให้Car
ทั้งคู่Engine
และ a Wheel
)
แต่บางครั้งก็ใช่ และในเวลานั้นไม่มีอะไรจะทำงานได้ดีกว่า MI
แต่เนื่องจาก MI มีกลิ่นเหม็นเตรียมพร้อมที่จะปกป้องสถาปัตยกรรมของคุณในการตรวจสอบโค้ด (และปกป้องมันเป็นสิ่งที่ดีเพราะถ้าคุณไม่สามารถปกป้องมันได้คุณควรไม่ทำ)
จากการสัมภาษณ์กับ Bjarne Stroustrup :
ผู้คนพูดอย่างถูกต้องว่าคุณไม่ต้องการรับมรดกหลายอย่างเพราะคุณสามารถทำอะไรได้หลายอย่างกับมรดกคุณสามารถทำได้ด้วยการรับมรดกเดียว คุณเพียงแค่ใช้เคล็ดลับการมอบหมายที่ฉันกล่าวถึง นอกจากนี้คุณไม่จำเป็นต้องรับมรดกใด ๆ เลยเพราะสิ่งที่คุณทำกับมรดกเดียวที่คุณสามารถทำได้โดยไม่ต้องรับมรดกโดยการส่งต่อผ่านชั้นเรียน ที่จริงแล้วคุณไม่ต้องการคลาสใด ๆ เพราะคุณสามารถทำได้ด้วยพอยน์เตอร์และโครงสร้างข้อมูล แต่ทำไมคุณต้องการทำเช่นนั้น? เมื่อใดสะดวกในการใช้สิ่งอำนวยความสะดวกด้านภาษา? คุณต้องการวิธีแก้ปัญหาเมื่อใด ฉันเคยเห็นกรณีที่มรดกหลายอย่างมีประโยชน์และฉันเคยเห็นกรณีที่การสืบทอดหลายมรดกค่อนข้างซับซ้อนมีประโยชน์ โดยทั่วไปแล้วฉันชอบที่จะใช้สิ่งอำนวยความสะดวกที่มีให้โดยภาษาในการแก้ปัญหา
const
- ฉันต้องเขียนวิธีแก้ปัญหาแบบ clunky (โดยปกติจะใช้ส่วนต่อประสานและองค์ประกอบ)เมื่อชั้นเรียนจำเป็นต้องมีตัวแปรที่ไม่แน่นอนและไม่เปลี่ยนรูป อย่างไรก็ตามฉันไม่เคยพลาดการรับมรดกหลายครั้งและไม่เคยรู้สึกว่าฉันต้องเขียนวิธีแก้ปัญหาเนื่องจากขาดคุณสมบัตินี้ นั่นคือความแตกต่าง ในทุกกรณีที่ฉันเคยเห็นไม่ได้ใช้ MI เป็นตัวเลือกที่ดีกว่าการออกแบบไม่ได้แก้ปัญหา
ไม่มีเหตุผลที่จะหลีกเลี่ยงและมันมีประโยชน์มากในสถานการณ์ คุณจำเป็นต้องตระหนักถึงปัญหาที่อาจเกิดขึ้น
สิ่งที่ยิ่งใหญ่ที่สุดคือเพชรแห่งความตาย:
class GrandParent;
class Parent1 : public GrandParent;
class Parent2 : public GrandParent;
class Child : public Parent1, public Parent2;
ตอนนี้คุณมี "สำเนา" ของ GrandParent ภายในชุดย่อย
C ++ มีความคิดในเรื่องนี้และให้คุณทำเสมือนการสืบทอดเพื่อแก้ไขปัญหา
class GrandParent;
class Parent1 : public virtual GrandParent;
class Parent2 : public virtual GrandParent;
class Child : public Parent1, public Parent2;
ตรวจสอบการออกแบบของคุณเสมอให้แน่ใจว่าคุณไม่ได้ใช้การสืบทอดเพื่อบันทึกในการใช้ข้อมูลซ้ำ หากคุณสามารถแสดงสิ่งเดียวกันกับองค์ประกอบ (และโดยทั่วไปแล้วคุณสามารถทำได้) นี่เป็นวิธีที่ดีกว่ามาก
GrandParent
Child
มีความกลัว MI เพราะคนคิดว่าพวกเขาอาจไม่เข้าใจกฎภาษา แต่ทุกคนที่ไม่สามารถได้รับกฎง่ายๆเหล่านี้ก็ไม่สามารถเขียนโปรแกรมที่ไม่สำคัญ
ดูที่ w: การสืบทอดหลายอย่าง
การสืบทอดหลายครั้งได้รับการวิจารณ์และไม่ได้นำมาใช้ในหลายภาษา การวิจารณ์รวมถึง:
- เพิ่มความซับซ้อน
- ความคลุมเครือความหมายมักจะสรุปเป็นปัญหาเพชร
- ไม่สามารถสืบทอดหลายครั้งจากคลาสเดียวอย่างชัดเจน
- ลำดับของการเปลี่ยนความหมายคลาสที่สืบทอด
การสืบทอดหลายภาษาในภาษาที่มีตัวสร้างสไตล์ C ++ / Java จะทำให้ปัญหาการสืบทอดของคอนสตรัคเตอร์และการเชื่อมโยงคอนสตรัคยิ่งทวีความรุนแรงยิ่งขึ้น วัตถุที่สัมพันธ์กับการสืบทอดด้วยวิธีการก่อสร้างที่แตกต่างกันอย่างมากนั้นยากที่จะนำไปใช้ภายใต้กระบวนทัศน์ของการกำหนดสายงาน
วิธีที่ทันสมัยในการแก้ไขปัญหานี้เพื่อใช้อินเทอร์เฟซ (คลาสนามธรรมที่เป็นนามธรรม) เช่น COM และ Java อินเตอร์เฟส
ฉันสามารถทำสิ่งอื่นแทนสิ่งนี้ได้ไหม
ใช่คุณสามารถ. ฉันกำลังจะไปขโมยจากGoF
การสืบทอดสาธารณะเป็นความสัมพันธ์แบบ IS-A และบางครั้งคลาสจะเป็นประเภทของคลาสที่แตกต่างกันหลายครั้งและบางครั้งก็เป็นสิ่งสำคัญที่จะสะท้อนสิ่งนี้
"Mixins" ก็มีประโยชน์เช่นกัน โดยทั่วไปแล้วจะเป็นชั้นเรียนขนาดเล็กซึ่งมักจะไม่สืบทอดจากสิ่งใด ๆ
ตราบใดที่ลำดับชั้นการสืบทอดค่อนข้างตื้น (และเกือบจะทุกครั้ง) และมีการจัดการที่ดีคุณไม่น่าจะได้รับมรดกเพชรที่น่ากลัว ข้าวหลามตัดไม่ได้มีปัญหากับทุกภาษาที่ใช้การสืบทอดหลายแบบ แต่การรักษา C ++ นั้นค่อนข้างแปลกและบางครั้งก็ทำให้งงงวย
ในขณะที่ฉันพบเจอกรณีที่การสืบทอดหลายอย่างมีประโยชน์มากจริง ๆ แล้วเป็นของหายาก อาจเป็นเพราะฉันต้องการใช้วิธีการออกแบบอื่นเมื่อฉันไม่ต้องการรับมรดกหลาย ๆ ฉันชอบที่จะหลีกเลี่ยงการสร้างภาษาที่สับสนและเป็นเรื่องง่ายที่จะสร้างกรณีการสืบทอดที่คุณต้องอ่านคู่มือให้ดีเพื่อที่จะเข้าใจว่าเกิดอะไรขึ้น
คุณไม่ควร "หลีกเลี่ยง" การสืบทอดหลายอย่าง แต่คุณควรตระหนักถึงปัญหาที่อาจเกิดขึ้นเช่น 'ปัญหาเพชร' ( http://en.wikipedia.org/wiki/Diamond_problem ) และรักษาพลังที่คุณได้รับด้วยความระมัดระวัง เท่าที่คุณควรจะมีพลังทั้งหมด
ด้วยความเสี่ยงที่จะเกิดความเป็นนามธรรมขึ้นมาฉันคิดว่ามันเป็นความคิดที่น่าสนใจเกี่ยวกับการสืบทอดภายในกรอบของทฤษฎีหมวดหมู่
หากเรานึกถึงคลาสและลูกศรทั้งหมดของเราที่อยู่ระหว่างพวกเขาเพื่อแสดงถึงความสัมพันธ์ในการถ่ายทอดทางพันธุกรรมสิ่งนี้
A --> B
หมายความว่ามาจากclass B
class A
โปรดทราบว่าให้
A --> B, B --> C
เราบอกว่า C มาจาก B ซึ่งมาจาก A ดังนั้น C ก็บอกว่ามาจาก A ดังนั้น
A --> C
ยิ่งกว่านั้นเราบอกว่าสำหรับทุกชั้นเรียนA
ที่A
มาจากแบบเล็กน้อยA
ดังนั้นรูปแบบการสืบทอดของเราจึงทำให้คำจำกัดความของหมวดหมู่เป็นจริง ในภาษาดั้งเดิมมากขึ้นเรามีหมวดหมู่ที่Class
มีวัตถุทุกชั้นและ morphisms ความสัมพันธ์ของการสืบทอด
นั่นคือการตั้งค่าเล็กน้อย แต่ถ้าอย่างนั้นเรามาดู Diamond of Doom ของเรา:
C --> D
^ ^
| |
A --> B
มันเป็นแผนภาพดูร่มรื่น แต่มันจะทำ ดังนั้นD
สืบทอดจากทั้งหมดของA
, และB
C
นอกจากนี้และได้รับการใกล้ชิดเพื่อที่อยู่คำถามของ OP, D
ยังสืบทอดจาก superclass A
ใด เราสามารถวาดไดอะแกรม
C --> D --> R
^ ^
| |
A --> B
^
|
Q
ตอนนี้ปัญหาที่เกี่ยวข้องกับ Diamond of Death ที่นี่คือเมื่อใดC
และB
แบ่งปันชื่อทรัพย์สิน / วิธีการและสิ่งต่าง ๆ ที่คลุมเครือ อย่างไรก็ตามหากเราย้ายพฤติกรรมที่ใช้ร่วมกันไปสู่A
ความคลุมเครือจะหายไป
ใส่ในแง่เด็ดขาดเราต้องการA
, B
และC
เป็นเช่นว่าถ้าB
และC
สืบทอดจากQ
นั้นสามารถเขียนเท่าที่เป็นรองA
Q
นี้จะทำให้A
สิ่งที่เรียกว่าpushout
นอกจากนี้ยังมีการก่อสร้างสมมาตรบนD
เรียกว่าดึง นี้เป็นหลักใหญ่ระดับประโยชน์ทั่วไปที่คุณสามารถสร้างซึ่งสืบทอดจากทั้งสองและB
C
นั่นคือถ้าคุณมีระดับอื่น ๆR
คูณสืบทอดจากB
และC
จากนั้นD
เป็นชั้นที่สามารถเขียนเท่าที่เป็นรองR
D
ตรวจสอบให้แน่ใจว่าเคล็ดลับของเพชรของคุณคือการดึงกลับและการกดเพื่อให้เรามีวิธีที่ดีในการจัดการปัญหาการตั้งชื่อหรือการบำรุงรักษาที่อาจเกิดขึ้นเป็นอย่างอื่น
หมายเหตุ คำตอบของPaercebalเป็นแรงบันดาลใจในเรื่องนี้เนื่องจากคำตักเตือนของเขาถูกบอกเป็นนัยโดยโมเดลข้างต้นเนื่องจากเราทำงานในหมวดหมู่ทั้งหมดของชั้นเรียนที่เป็นไปได้ทั้งหมด
ฉันต้องการพูดคุยเรื่องของเขากับสิ่งที่แสดงให้เห็นว่าความสัมพันธ์ของการสืบทอดที่ซับซ้อนหลายอย่างนั้นมีทั้งที่มีประสิทธิภาพและไม่มีปัญหาได้อย่างไร
TL; DRคิดถึงความสัมพันธ์ในการรับมรดกในโปรแกรมของคุณในรูปของหมวดหมู่ จากนั้นคุณสามารถหลีกเลี่ยงปัญหาของ Diamond of Doom ได้โดยการเพิ่มคลาสที่สืบทอดมาหลายครั้งและทำให้เป็นคลาสแม่แบบทั่วไปซึ่งเป็นการดึงกลับ
เราใช้ไอเฟล เรามี MI ที่ยอดเยี่ยม ไม่ต้องห่วง. ไม่มีปัญหา จัดการได้อย่างง่ายดาย มีบางครั้งที่จะไม่ใช้ MI อย่างไรก็ตามมันมีประโยชน์มากกว่าที่ผู้คนจะรู้เพราะ: A) ในภาษาที่อันตรายที่ไม่สามารถจัดการมันได้ดี - หรือ - B) พอใจกับวิธีที่พวกเขาได้ทำงานกับ MI มาหลายปีและหลายปี - หรือ - เหตุผลอื่น ๆ ( มากเกินไปที่จะแสดงรายการฉันค่อนข้างแน่ใจ - ดูคำตอบด้านบน)
สำหรับเราแล้วการใช้ Eiffel, MI นั้นเป็นเรื่องธรรมดาเหมือนอย่างอื่นและเป็นเครื่องมือที่ดีในกล่องเครื่องมือ ตรงไปตรงมาเราค่อนข้างไม่สนใจว่าไม่มีใครใช้ Eiffel ไม่ต้องห่วง. เรามีความสุขกับสิ่งที่เรามีและเชิญชวนให้คุณดู
ในขณะที่คุณกำลังมองหา: จดบันทึกเป็นพิเศษเกี่ยวกับความปลอดภัยเป็นโมฆะและการกำจัดของตัวชี้ Null dereferencing ในขณะที่เรากำลังเต้นรำไปรอบ ๆ MI พอยน์เตอร์ของคุณก็หลงทาง! :-)
ภาษาโปรแกรมทุกภาษามีความแตกต่างกันเล็กน้อยในการเขียนโปรแกรมเชิงวัตถุด้วยข้อดีข้อเสีย เวอร์ชันของ C ++ ให้ความสำคัญกับประสิทธิภาพการทำงานอย่างเต็มที่และมีข้อเสียที่ทำให้การเขียนโค้ดไม่ถูกต้องเป็นเรื่องง่าย - และนี่เป็นเรื่องจริงของการสืบทอดหลายครั้ง ดังนั้นจึงมีแนวโน้มที่จะคัดโปรแกรมเมอร์ออกจากคุณลักษณะนี้
คนอื่น ๆ ได้ตอบคำถามเกี่ยวกับสิ่งที่หลายมรดกไม่ดีสำหรับ แต่เราได้เห็นความคิดเห็นบางส่วนที่มากหรือน้อยแสดงว่าเหตุผลที่ควรหลีกเลี่ยงเพราะมันไม่ปลอดภัย ก็ใช่และไม่ใช่
บ่อยครั้งที่เป็นจริงใน C ++ หากคุณทำตามแนวทางพื้นฐานคุณสามารถใช้งานได้อย่างปลอดภัยโดยไม่ต้อง "มองข้ามไหล่ของคุณ" อย่างต่อเนื่อง แนวคิดหลักคือคุณต้องแยกแยะคำจำกัดความของคลาสชนิดพิเศษที่เรียกว่า "มิกซ์อิน" class เป็นการผสมผสานหากฟังก์ชันสมาชิกทั้งหมดเป็นเสมือน (หรือเสมือนจริง) จากนั้นคุณจะได้รับอนุญาตให้สืบทอดจากคลาสหลักเดียวและ "มิกซ์อิน" ได้มากเท่าที่คุณต้องการ - แต่คุณควรสืบทอดมิกซ์อินด้วยคำหลัก "เสมือน" เช่น
class CounterMixin {
int count;
public:
CounterMixin() : count( 0 ) {}
virtual ~CounterMixin() {}
virtual void increment() { count += 1; }
virtual int getCount() { return count; }
};
class Foo : public Bar, virtual public CounterMixin { ..... };
ข้อเสนอแนะของฉันคือถ้าคุณตั้งใจจะใช้คลาสเป็นแบบมิกซ์อินคุณยังใช้ระเบียบการตั้งชื่อเพื่อให้ง่ายสำหรับทุกคนที่อ่านโค้ดเพื่อดูว่าเกิดอะไรขึ้น & เพื่อตรวจสอบว่าคุณกำลังเล่นตามกฎของแนวทางพื้นฐาน . และคุณจะพบว่ามันทำงานได้ดีขึ้นมากถ้ามิกซ์อินของคุณมีคอนสตรัคเตอร์เริ่มต้นด้วยเช่นกันเพราะวิธีการทำงานของคลาสฐานเสมือน และอย่าลืมที่จะทำให้ destructors ทั้งหมดเป็นจริงด้วย
โปรดทราบว่าการใช้คำว่า "มิกซ์อิน" ของฉันที่นี่ไม่เหมือนกับคลาสเทมเพลตที่กำหนดพารามิเตอร์ (ดูลิงก์นี้สำหรับคำอธิบายที่ดี) แต่ฉันคิดว่านี่เป็นการใช้คำศัพท์อย่างเป็นธรรม
ตอนนี้ฉันไม่ต้องการให้ความประทับใจว่านี่เป็นวิธีเดียวที่จะใช้การสืบทอดหลายอย่างได้อย่างปลอดภัย เป็นวิธีเดียวที่ตรวจสอบได้ค่อนข้างง่าย
คุณควรใช้อย่างระมัดระวังมีบางกรณีเช่นปัญหาเพชรเมื่อสิ่งต่าง ๆ มีความซับซ้อน
(ที่มา: learncpp.com )
Printer
PoweredDevice
A Printer
สำหรับการพิมพ์ไม่ใช่การจัดการพลังงาน การใช้งานเครื่องพิมพ์เฉพาะอาจต้องทำการจัดการพลังงานบางอย่าง แต่คำสั่งการจัดการพลังงานเหล่านี้ไม่ควรเปิดเผยโดยตรงกับผู้ใช้เครื่องพิมพ์ ฉันไม่สามารถจินตนาการถึงการใช้ลำดับชั้นนี้ในชีวิตจริงได้
การใช้และการใช้ในทางที่ผิดจากมรดก
บทความนี้อธิบายการสืบทอดได้เป็นอย่างดีและเป็นอันตราย
นอกเหนือจากรูปแบบเพชรแล้วการสืบทอดหลายครั้งมักทำให้รูปแบบวัตถุนั้นยากที่จะเข้าใจซึ่งจะเพิ่มค่าบำรุงรักษา
องค์ประกอบนั้นง่ายต่อการเข้าใจเข้าใจและอธิบายโดยเนื้อแท้ มันน่าเบื่อที่จะเขียนโค้ดสำหรับ แต่ IDE ที่ดี (มันไม่กี่ปีที่ผ่านมาตั้งแต่ฉันทำงานกับ Visual Studio แต่แน่นอนว่า Java IDEs ทั้งหมดมีเครื่องมือทางลัดองค์ประกอบอัตโนมัติที่ยอดเยี่ยม) ควรจะได้รับคุณข้ามอุปสรรค์นั้น
นอกจากนี้ในแง่ของการบำรุงรักษา "ปัญหาเพชร" เกิดขึ้นในอินสแตนซ์ที่ไม่ใช่ตัวอักษรเช่นกัน ตัวอย่างเช่นหากคุณมี A และ B และคลาส C ของคุณขยายทั้งสองและ A มีวิธี 'makeJuice' ซึ่งทำให้น้ำส้มและคุณขยายที่จะทำให้น้ำส้มกับมะนาวบิด: เกิดอะไรขึ้นเมื่อนักออกแบบสำหรับ ' B 'เพิ่มวิธี' makeJuice 'ซึ่งสร้างและกระแสไฟฟ้า? 'A' และ 'B' อาจเข้ากันได้กับ "ผู้ปกครอง" ในตอนนี้แต่นั่นไม่ได้หมายความว่าพวกเขาจะเป็นเช่นนั้นเสมอ!
โดยรวมแล้วจำนวนสูงสุดของการดูแลเพื่อหลีกเลี่ยงการสืบทอดและโดยเฉพาะอย่างยิ่งการรับมรดกหลายรายการเป็นเสียง ในฐานะ maxims ทั้งหมดมีข้อยกเว้น แต่คุณต้องตรวจสอบให้แน่ใจว่ามีสัญญาณไฟนีออนสีเขียวที่กระพริบชี้ไปที่ข้อยกเว้นใด ๆ ที่คุณเขียนโค้ด (และฝึกสมองของคุณเพื่อให้ทุกครั้งที่คุณเห็นต้นไม้มรดกที่คุณวาด ลงชื่อเข้าใช้) และให้คุณตรวจสอบเพื่อให้แน่ใจว่าเหมาะสมทุกครั้ง
what happens when the designer for 'B' adds a 'makeJuice' method which generates and electrical current?
เอ่อคุณได้รับข้อผิดพลาดในการรวบรวมแน่นอน (ถ้าใช้ไม่ชัดเจน)
ปัญหาสำคัญกับ MI ของวัตถุที่เป็นรูปธรรมก็คือคุณไม่ค่อยมีวัตถุที่ถูกต้องตามกฎหมายควร "เป็น A และเป็น B" ดังนั้นจึงไม่ค่อยแก้ปัญหาที่ถูกต้องในเหตุผลเชิงตรรกะ บ่อยครั้งที่คุณมีวัตถุ C ที่เชื่อฟัง "C สามารถทำหน้าที่เป็น A หรือ B" ซึ่งคุณสามารถบรรลุได้ผ่านการสืบทอดและองค์ประกอบของส่วนต่อประสาน แต่ไม่ผิดพลาด - การสืบทอดของอินเตอร์เฟสหลาย ๆ ตัวยังคงเป็น MI เพียงส่วนหนึ่งของมัน
สำหรับ C ++ โดยเฉพาะจุดอ่อนที่สำคัญของคุณสมบัติไม่ใช่การมีอยู่จริงของการสืบทอดหลายจุด แต่มีโครงสร้างบางอย่างที่อนุญาตให้มีความผิดปกติเกือบทุกครั้ง ตัวอย่างเช่นการสืบทอดหลาย ๆ สำเนาของวัตถุเดียวกันเช่น:
class B : public A, public A {};
ผิดรูปแบบโดยคำจำกัดความ แปลเป็นภาษาอังกฤษนี่คือ "B คือ A และ A" ดังนั้นแม้ในภาษามนุษย์มีความกำกวมอย่างรุนแรง คุณหมายถึง "B has 2 As" หรือเพียงแค่ "B is A"? การอนุญาตให้ใช้รหัสทางพยาธิวิทยาดังกล่าวและทำให้ตัวอย่างเป็นตัวอย่างการใช้งานที่แย่ลงทำให้ C ++ ไม่ได้รับประโยชน์เมื่อมันมาถึงการสร้างเคสเพื่อรักษาคุณลักษณะในภาษาที่สืบทอดมา
คุณสามารถใช้องค์ประกอบในการตั้งค่าการสืบทอด
ความรู้สึกทั่วไปคือการจัดองค์ประกอบที่ดีกว่าและมันเป็นเรื่องที่ดีมาก
The general feeling is that composition is better, and it's very well discussed.
นั่นไม่ได้หมายความว่าองค์ประกอบจะดีกว่า
ใช้เวลา 4/8 ไบต์ต่อคลาสที่เกี่ยวข้อง (หนึ่งตัวชี้นี้ต่อคลาส)
สิ่งนี้อาจไม่น่าเป็นห่วง แต่ถ้าวันหนึ่งคุณมีโครงสร้างข้อมูลขนาดเล็กซึ่งมีเวลานับพันล้านครั้ง