ฉันควรหลีกเลี่ยงการใช้การสืบทอดของวัตถุเป็นไปได้ในการพัฒนาเกมหรือไม่?


36

ฉันชอบฟีเจอร์ OOP เมื่อพัฒนาเกมด้วย Unity ฉันมักจะสร้างคลาสพื้นฐาน (ส่วนใหญ่เป็นนามธรรม) และใช้การสืบทอดวัตถุเพื่อแบ่งปันฟังก์ชันการทำงานเดียวกันกับวัตถุอื่น ๆ

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

ฉันใช้คลาสเบสที่เป็นนามธรรมบางอย่างเช่นWeaponBaseและสร้างคลาสอาวุธที่เฉพาะเจาะจงเช่นShotgunนั้นAssaultRifleเป็นMachinegunอย่างนั้น มีข้อดีมากมายและรูปแบบเดียวที่ฉันชอบคือความแตกต่าง ฉันสามารถปฏิบัติต่อวัตถุที่สร้างโดยคลาสย่อยราวกับว่าพวกเขาเป็นคลาสพื้นฐานเพื่อให้ตรรกะสามารถลดลงอย่างมากและทำให้สามารถนำมาใช้ซ้ำได้มากขึ้น หากฉันต้องการเข้าถึงฟิลด์หรือวิธีการของคลาสย่อยฉันจะส่งกลับไปยังคลาสย่อย

ฉันไม่ต้องการที่จะกำหนดเขตข้อมูลเดียวกันที่นิยมใช้ในการเรียนแตกต่างกันเช่นAudioSource/ Rigidbody/ Animatorและจำนวนมากของเขตข้อมูลสมาชิกฉันที่กำหนดไว้เช่นfireRate, damage, range, spreadและอื่น ๆ ในบางกรณีวิธีการบางอย่างอาจถูกเขียนทับ ดังนั้นฉันใช้วิธีเสมือนในกรณีนั้นโดยทั่วไปฉันสามารถเรียกใช้วิธีนั้นโดยใช้วิธีการจากคลาสผู้ปกครอง แต่ถ้าตรรกะควรจะแตกต่างกันในเด็กฉันได้แทนที่พวกเขาด้วยตนเอง

ดังนั้นสิ่งเหล่านี้ทั้งหมดควรถูกทอดทิ้งเป็นการปฏิบัติที่ไม่ดี? ฉันควรใช้อินเทอร์เฟซแทนหรือไม่


30
"มีข้อดีมากมายและรูปแบบเดียวที่ฉันชอบคือ polymorphism" ความแตกต่างไม่ใช่รูปแบบ เป็นจุดรวมของ OOP อย่างแท้จริง สิ่งอื่น ๆ เช่นเขตข้อมูลทั่วไปและอื่น ๆ สามารถทำได้อย่างง่ายดายโดยไม่ต้องทำซ้ำรหัสหรือมรดก
Cubic

3
คนที่แนะนำอินเทอร์เฟซของคุณดีกว่าการสืบทอดให้ข้อโต้แย้งใด ๆ กับคุณหรือไม่? ฉันอยากรู้.
Hawker65

1
@ Cubic ฉันไม่เคยพูดว่า polymorphism เป็นรูปแบบ ฉันพูดว่า "... รูปแบบหนึ่งที่ฉันชอบจริงๆคือความแตกต่าง" หมายความว่ารูปแบบที่ฉันชอบจริงๆคือการใช้ "ความหลากหลาย" ไม่ใช่รูปแบบที่หลากหลาย
ออกแบบใหม่

4
ผู้คนมักใช้อินเทอร์เฟซมากกว่าการสืบทอดในรูปแบบการพัฒนาใด ๆ อย่างไรก็ตามมันมักจะเพิ่มค่าใช้จ่ายจำนวนมากความไม่ลงรอยกันทางปัญญานำไปสู่การระเบิดในชั้นเรียนมักจะเพิ่มคุณค่าที่น่าสงสัยและมักจะไม่ประสานกันได้ดีในระบบเว้นแต่ว่าทุกคนในโครงการจะมุ่งเน้นไปที่อินเตอร์เฟส ดังนั้นอย่าละทิ้งมรดกเลย อย่างไรก็ตามเกมมีลักษณะเฉพาะบางอย่างและสถาปัตยกรรมของ Unity คาดหวังให้คุณใช้กระบวนทัศน์การออกแบบในงาน CES ซึ่งเป็นเหตุผลที่คุณควรปรับแต่งแนวทางของคุณ อ่านชุดพ่อมดและนักรบของ Eric Lippert เพื่ออธิบายปัญหาประเภทเกม
Dunk

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

คำตอบ:


65

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

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

Widget->Button->SpinnerButton หรือ

ConnectionManager->TCPConnectionManager->UDPConnectionManagerVS

... มีลำดับชั้นที่ชัดเจนของการสืบทอดมาที่นี่มากกว่าการพิสูจน์ที่เป็นไปได้มากมายดังนั้นมันจึงง่ายต่อการใช้การสืบทอด

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


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

1
@Ruslan ฉันได้เพิ่มคำว่า 'may' และ 'can' เพื่อรองรับความคิดเห็นของคุณ มิฉะนั้นเพื่อผลประโยชน์ในการรักษาคำตอบให้กระชับฉันไม่ได้ลงรายละเอียดมากเกินไป ขอบคุณ
วิศวกร

7
P.S. The other reason we may favour composition in entity systems is ... performance: จริงเหรอ? ดูที่หน้า WIkipedia ที่ลิงก์โดย @Linaith แสดงให้เห็นว่าคุณต้องเขียนวัตถุอินเทอร์เฟซของคุณ ดังนั้นคุณมีการเรียกใช้ฟังก์ชันเสมือน (หรือมากกว่า) เสมือนและคิดถึงแคชมากกว่าเดิมเมื่อคุณแนะนำการอ้อมอีกระดับ
Flamefire

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

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

18

คุณได้คำตอบที่ดีอยู่แล้ว แต่ช้างตัวใหญ่ในห้องในคำถามของคุณคืออันนี้:

ได้ยินจากคนที่ใช้การสืบทอดต้องหลีกเลี่ยงและเราควรใช้อินเทอร์เฟซแทน

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

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

คลัปต่ำ

ซึ่งหมายความว่า "มัดสิ่ง" ในรหัสของคุณควรขึ้นอยู่กับสภาพแวดล้อมของมันให้น้อยที่สุด สิ่งนี้ใช้สำหรับคลาส (การออกแบบคลาส) แต่ยังรวมถึงออบเจกต์ (การใช้งานจริง), "ไฟล์" โดยทั่วไป (เช่นจำนวนของ#includes ต่อ.cppไฟล์เดียว, จำนวนไฟล์importต่อ.javaไฟล์เดียวเป็นต้น)

สัญญาณที่สองหน่วยงานเชื่อมต่อกันคือหนึ่งในนั้นจะแตก (หรือจำเป็นต้องเปลี่ยนแปลง) เมื่ออีกฝ่ายถูกเปลี่ยนแปลงในทางใดทางหนึ่ง

มรดกเพิ่มการมีเพศสัมพันธ์อย่างชัดเจน การเปลี่ยนคลาสพื้นฐานจะเปลี่ยนคลาสย่อยทั้งหมด

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

การติดต่อกันสูง

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

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

สัญญาณที่บ่งบอกว่าเอนทิตีไม่เหนียวแน่นคือถ้ามันใหญ่ขึ้นและใหญ่ขึ้นแยกออกไปในหลายทิศทางในเวลาเดียวกัน

การรับมรดกในขณะที่คุณอธิบายมันลดการทำงานร่วมกัน - ชั้นเรียนอาวุธของคุณในตอนท้ายของวันชิ้นใหญ่ที่จัดการทุกด้านที่แตกต่างและไม่เกี่ยวข้องกับอาวุธของคุณ

อินเทอร์เฟซทางอ้อมเพิ่มการเชื่อมโยงกันโดยแยกความรับผิดชอบออกอย่างชัดเจนระหว่างสองด้านของอินเทอร์เฟซ

สิ่งที่ต้องทำตอนนี้

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

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


ขอบคุณสำหรับคำอธิบายโดยละเอียด มันมีประโยชน์จริง ๆ ที่จะเข้าใจสิ่งที่ฉันหายไป
modernator

13

ความคิดที่ว่าต้องหลีกเลี่ยงการสืบทอดนั้นเป็นสิ่งที่ผิด

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

ฉันต้องบอกว่าฉันชอบคลาสอาวุธของคุณและมันจะทำแบบเดียวกัน แต่ตอนนี้ฉันยังไม่ได้สร้างเกม ...

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


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

3
@JamesTrotter ณ จุดนี้ผมคิดว่า Borderlands และปืนของตนและสิ่งที่น่าแปลกใจประหลาดนี้จะมีการถ่ายทอดทางพันธุกรรมเพียง แต่ ...
Baldrickk

1
@JamesTrotter ฉันหวังว่าจะเป็นคำตอบและไม่ใช่ความคิดเห็น
Pharap

6

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

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

สิ่งที่เกี่ยวกับ AssultRifle กับ GrenadeLauncher ภายใต้ - อืมมันยากขึ้นเล็กน้อย คุณสามารถแทนที่ GrenadeLauncher เป็นไฟฉายสำหรับล่าสัตว์ตอนกลางคืนได้หรือไม่?

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

การสืบทอดหลายอย่างสามารถแก้ไขตัวอย่างเล็กน้อยเหล่านี้ได้บ้าง แต่มันเพิ่มปัญหาชุดของตัวเองลงไป

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


2
"คุณจะอนุญาตให้ผู้ใช้สร้างปืนก่อนหน้าโดยแก้ไขไฟล์กำหนดค่าอย่างไร" -> รูปแบบที่ฉันทำโดยทั่วไปคือสร้างคลาสสุดท้ายสำหรับอาวุธแต่ละชนิด ตัวอย่างเช่น Glock17 สร้างคลาส WeaponBase ก่อน คลาสนี้ถูกทำให้เป็นนามธรรมกำหนดค่าทั่วไปเช่นโหมดไฟอัตราการยิงขนาดนิตยสารกระสุนสูงสุดเม็ด นอกจากนี้ยังจัดการอินพุตของผู้ใช้เช่น mouse1 / mouse2 / reload และเรียกใช้เมธอดที่สอดคล้องกัน พวกเขาเกือบเสมือนหรือแยกออกเป็นฟังก์ชั่นเพิ่มเติมเพื่อให้นักพัฒนาสามารถแทนที่ฟังก์ชั่นพื้นฐานหากพวกเขาต้องการ จากนั้นสร้างคลาสอีกคลาสที่ขยาย WeaponBase,
ออกแบบสมัยใหม่

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

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

7
@modernator ใน Minecraft พวกเขาทำMonstersubclass WalkingEntityของ จากนั้นพวกเขาเพิ่ม Slimes คุณคิดว่ามันใช้งานได้ดีแค่ไหน?
user253751

1
@immibis คุณมีการอ้างอิงหรือลิงค์ไปยังประวัติปัญหาหรือไม่? คำหลัก Minecraft ของ Googling จมน้ำตายในลิงค์วิดีโอ ;-)
Peter - Reinstate Monica

3

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

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

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


ถ้าส่วนต่อประสานนั้นเป็นพลเมืองชั้นหนึ่งของภาษา OOP ฉันสงสัยว่า Python, Ruby, …ไม่ใช่ภาษา OOP
BlackJack

@BlackJack: ในทับทิมอินเทอร์เฟซนั้นเป็นนัย (การพิมพ์เป็ดถ้าคุณจะ) และแสดงโดยใช้องค์ประกอบแทนการสืบทอด (ยังมี Mixins ซึ่งอยู่ที่ไหนสักแห่งในระหว่างเท่าที่ฉันกังวล) ...
AnoE

@BlackJack "Interface" (แนวคิด) ไม่ต้องการinterface(คำหลักภาษา)
Caleth

2
@Caleth แต่เรียกพวกเขาว่าพลเมืองชั้นหนึ่ง IMHO เมื่อคำอธิบายภาษาอ้างว่าบางสิ่งเป็นพลเมืองชั้นหนึ่งก็มักจะหมายความว่าเป็นเรื่องจริงในภาษานั้นที่มีประเภทและคุณค่าและสามารถส่งผ่านได้ Like คลาสเป็นพลเมืองชั้นหนึ่งใน Python เช่นเดียวกับฟังก์ชั่นวิธีการและโมดูล ถ้าเรากำลังพูดถึงแนวคิดเอาล่ะอินเทอร์เฟซก็เป็นส่วนหนึ่งของภาษาที่ไม่ใช่ OOP ทั้งหมดด้วย
BlackJack

2

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

การผสมผสานระหว่างการสืบทอดเป็นคำถามแบบ is-a vs has-a อินเทอร์เฟซยังเป็นอีกวิธีหนึ่ง (ที่ 3) ซึ่งเหมาะสมกับบางสถานการณ์

การสืบทอดหรือตรรกะคือ: คุณใช้เมื่อคลาสใหม่กำลังทำงานและใช้เหมือนคลาสเก่า (ภายนอก) อย่างสมบูรณ์ที่คุณได้รับมาหากคลาสใหม่กำลังจะเปิดเผยต่อสาธารณะ ฟังก์ชั่นทั้งหมดที่ชั้นเรียนเก่ามี ... แล้วคุณจะสืบทอด

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

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

สมมติว่าคุณมีสิ่งมีชีวิตหลายชนิดที่แสดงโดยคลาสและพวกมันมีฟังก์ชันที่เป็นตัวแทนของฟังก์ชัน ตัวอย่างเช่นนกจะมี Talk (), Fly () และ Poop () เป็ดคลาสจะสืบทอดจากคลาสเบิร์ดโดยใช้คุณลักษณะทั้งหมด

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

อย่างไรก็ตามถ้าคุณแบ่งคุณสมบัติออกเป็นกลุ่มซึ่งเป็นตัวแทนของกลุ่มการโทรแต่ละกลุ่มด้วยอินเทอร์เฟซพูดว่า IFly ประกอบด้วย Takeoff (), FlapWings () และ Land () จากนั้นคุณสามารถทำได้สำหรับคลาส Fox ใช้ฟังก์ชันจาก ITalk และ IPoop แต่ไม่ใช่ IFly จากนั้นคุณจะกำหนดตัวแปรและพารามิเตอร์เพื่อยอมรับวัตถุที่ใช้อินเทอร์เฟซเฉพาะจากนั้นโค้ดที่ทำงานกับพวกมันจะรู้ว่ามันสามารถเรียกอะไร ... และสามารถสืบค้นอินเทอร์เฟซอื่น ๆ ได้เสมอหากต้องการดูฟังก์ชันอื่น ๆ ดำเนินการสำหรับวัตถุปัจจุบัน

วิธีการเหล่านี้แต่ละวิธีมีกรณีใช้งานเมื่อเป็นวิธีที่ดีที่สุดไม่มีวิธีใดวิธีหนึ่งเป็นทางออกที่ดีที่สุดสำหรับทุกกรณี


1

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

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

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

ในฐานะที่เป็นบันทึกด้านข้างฉันใช้ปัญหาบางอย่างกับสิ่งนี้:

ฉันมักจะสร้างคลาสพื้นฐาน (ส่วนใหญ่เป็นนามธรรม) และใช้การสืบทอดวัตถุเพื่อแบ่งปันฟังก์ชันการทำงานเดียวกันกับวัตถุอื่น ๆ

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

คุณสามารถใช้อินเทอร์เฟซถ้าคุณต้องการพูดมีList<IWeapon>อาวุธประเภทต่าง ๆ ด้วยวิธีนี้คุณสามารถรับช่วงต่อจากทั้ง IWeapon interface และ MonoBehaviour subclass สำหรับอาวุธทุกชนิดและหลีกเลี่ยงปัญหาใด ๆ หากไม่มีการสืบทอดหลายแบบ


-3

คุณดูเหมือนจะไม่เข้าใจว่าอินเตอร์เฟสคืออะไรหรือทำอะไร ฉันใช้เวลาคิดมากกว่า 10 ปีเกี่ยวกับ OO และถามคำถามผู้คนที่ฉลาดจริง ๆ ว่าฉันสามารถมีช่วงเวลา ah-ha ด้วยอินเทอร์เฟซ หวังว่านี่จะช่วยได้

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

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

อินเทอร์เฟซนั้นเป็นเพียงกลไกในการเชื่อมโยงโครงสร้าง OO ของคุณกับโครงสร้าง OO ที่แตกต่างกันกับแต่ละด้านที่มีคุณลักษณะที่แตกต่างกันซึ่งจะต้องเจรจา / แมปกับแต่ละอื่น ๆ

ดังนั้นสำหรับใจที่จะเรียกใช้ฟังก์ชั่น open-hand นั้นจะต้องใช้อินเตอร์เฟส nervous_command_system ด้วยการที่มี API ของตัวเองพร้อมคำแนะนำวิธีการใช้ข้อกำหนดทั้งหมดที่จำเป็นก่อนที่จะเรียก open-hand

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