การรับมรดกผิดไป


12

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

class Animal  
class Parrot : Animal 
class Elephant : Animal 
class Cow : Animal

เป็นต้น

คุณมีวิธีกิน (), รัน (), ฯลฯ และทุกอย่างดี วันหนึ่งมีคนเข้ามาและพูดว่า - คลาส CageBuilder ของเราทำงานได้ดีและใช้ animal.weight () และ animal.height () ยกเว้น African Bison ใหม่ที่แข็งแรงเกินไปและสามารถทุบกำแพงได้ดังนั้นฉันจะเพิ่ม อีกคุณสมบัติหนึ่งของคลาส Animal - isAfricanBizon () และใช้สิ่งนั้นเมื่อเลือกวัสดุและแทนที่มันสำหรับคลาส AfricanBizon เท่านั้น บุคคลต่อไปมาและทำสิ่งที่คล้ายกันและสิ่งต่อไปที่คุณรู้ว่าคุณมีคุณสมบัติเหล่านี้ทั้งหมดสำหรับชุดย่อยของลำดับชั้นในคลาสพื้นฐาน

เป็นวิธีที่ดีในการปรับปรุง / refactor รหัสดังกล่าวคืออะไร อีกทางเลือกหนึ่งที่นี่คือเพียงแค่ใช้ dynamic_casts เพื่อตรวจสอบประเภท แต่ที่ clutters ผู้โทรและเพิ่มพวงของ if-then-else ทั่วทุกสถานที่ คุณสามารถมีอินเทอร์เฟซเฉพาะเพิ่มเติมได้ที่นี่ แต่ถ้าคุณมีการอ้างอิงคลาสพื้นฐานที่ไม่ได้ช่วยอะไรมาก ข้อเสนอแนะอื่น ๆ ? ตัวอย่าง?

ขอบคุณ!


@ James: แล้วคุณจะต้องเขียน parsers ด้วยมือ : S
Matteo Italia

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

1
กินกระทิงทั้งหมดหรือไม่ [ฉันโพสต์ก่อนหน้านี้ แต่มันก็ถูกลบด้วยเหตุผลบางอย่างสันนิษฐานโดย jackasses ที่ไม่มีอารมณ์ขัน]
James McNellis

CageBuilder จำเป็นต้องมีคลาสของตัวเองหรือไม่? จะเกิดอะไรขึ้นถ้ามีเมธอด MakeCage ที่เป็นค่าเริ่มต้นซึ่งสามารถแก้ไขได้โดยแต่ละคลาส
งาน

1
คุณพูดถึงความยุ่งเหยิง if-then-else เป็นข้อเสียเปรียบสำหรับผู้โทร แต่ทันทีที่ผู้โทรเริ่มใช้ isAfricanBizon () พวกเขาจะถ่วงรหัสด้วย if-then-else โดยอัตโนมัติ ดังนั้นมันอาจจะยุ่งเหยิงถ้าเป็นอย่างอื่นกับ isAfricanBizon () หรือความยุ่งเหยิงอื่นที่มีการปลดเปลื้องแบบไดนามิก
davidk01

คำตอบ:


13

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


1
-1: เพียงบอกว่าสิ่งที่ไม่ได้จะทำอย่างไร OP ทราบแล้วว่านี่เป็นความคิดที่ไม่ดีดังนั้นคำถาม
Steven Evers

12

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

คุณต้องการเพิ่มวิธีการในอินเทอร์เฟซที่ตอบคำถามเฉพาะเสมอในกรณีนี้มันจะมีความแข็งแรง ()


+1: ดูเหมือนว่าคนอื่น ๆ จะทำลายแบบจำลองแนวคิดของคลาส (ซึ่งเป็นเพียงแค่แค็ปซูลคุณสมบัติของสัตว์ชนิดต่าง ๆ ) เพื่อรองรับกรณีการใช้งานเฉพาะนี้ strengthวิธีการอาจจะมีการสอบถามโดยปล่อยให้วิธีทำความสะอาดของการสนับสนุนที่แตกต่างกันของวัสดุกว่าmaterial.canHold(animal) ConcreteWall
Aidan Cully

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

3

ฉันคิดว่าปัญหาของคุณคือ: คุณมีไคลเอนต์ต่าง ๆ ของไลบรารีที่สนใจเฉพาะชุดย่อยของลำดับชั้น แต่ที่ผ่านตัวชี้ / อ้างอิงไปยังคลาสพื้นฐาน อันที่จริงแล้วปัญหาที่ dynamic_cast <> อยู่ที่นั่นเพื่อแก้ไข

มันเป็นเรื่องของการออกแบบของลูกค้าเพื่อลดการใช้ dynamic_cast <>; พวกเขาควรใช้มันเพื่อตรวจสอบว่าวัตถุนั้นต้องการการดูแลเป็นพิเศษหรือไม่และถ้าเป็นเช่นนั้นการดำเนินการทั้งหมดในการอ้างอิงแบบดาวน์ - คาสท์

หากคุณมีคอลเลกชันประเภท "มิกซ์อิน" ของฟังก์ชั่นที่ใช้กับหลาย ๆ ชั้นย่อยที่แยกจากกันคุณอาจต้องการใช้รูปแบบอินเตอร์เฟสที่ Java และ C # ใช้ มีคลาสพื้นฐานเสมือนที่เป็นคลาสเสมือนจริงและใช้ dynamic_cast <> เพื่อตรวจสอบว่าอินสแตนซ์มีการนำไปใช้งานหรือไม่


1

สิ่งหนึ่งที่คุณสามารถทำได้คือแทนที่การตรวจสอบที่ชัดเจนของประเภทเช่นมีการตรวจสอบคุณสมบัติที่คุณสนใจจริงคือisAfricanBison()isTooStrong()


1
isTooStrong () เพื่ออะไร คุณกำลังเพิ่มรหัสเฉพาะของกรงลงในคลาสสัตว์
Steven Evers

1

สัตว์ไม่ควรสนใจผนังคอนกรีต บางทีคุณสามารถแสดงมันด้วยค่าง่ายๆ

class Animal {
public:
  virtual ~Animal() {}
  virtual size_t height() const = 0;
  virtual size_t weight() const = 0;
  virtual bool isStrong() const = 0;
};

Cage *CreateCageFromSQL(Animal &a);
Cage *CreateCageFromOrangePeelsAndSticks(Animal &a);

ฉันสงสัยว่าไม่ได้เป็นไปได้ นั่นคือปัญหาเกี่ยวกับตัวอย่างของเล่น

ฉันจะไม่ต้องการเห็น RequireConcreteWalls () หรือบรรทัดและบรรทัดของตัวชี้แบบไดนามิกปลดเปลื้องในอัตราใด ๆ

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

class Animal {
public:
  virtual ~Animal() {}
  virtual CageBuilder *getCageBuilder() = 0;
};

สิ่งนี้ไม่ได้ขัดขวางคุณจากการใช้รหัสที่ใช้ร่วมกันเพียงแค่ทำให้สัตว์สกปรก

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

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

Double Dispatchเป็นอีกตัวเลือกหนึ่งแม้ว่าฉันจะลังเลที่จะกระโดดเข้าไปเสมอ

ยิ่งไปกว่านั้นมันยากที่จะคาดเดาปัญหา


0

attemptEscape()ดีแน่นอนสัตว์ทุกคนมีธรรมชาติของสถานที่ให้บริการ ในขณะที่บางวิธีการที่อาจก่อให้เกิดfalseผลในสถานการณ์ทั้งหมดในขณะที่คนอื่น ๆ อาจมีโอกาสตามออกของการวิเคราะห์พฤติกรรมของลักษณะที่แท้จริงของพวกเขาอื่น ๆ เช่นและsize weightแล้วแน่นอนในบางจุดกลายเป็นน่ารำคาญในขณะที่มันแน่นอนที่สุดจะกลับมาattemptEscape()true

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


-1

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



-2

ทำไมไม่ทำอะไรแบบนี้

class Animals { /***/ } class HeavyAnimals{} : Animals //The basic class for animals like the African Bison

ด้วยคลาส HeavyAnimals คุณสามารถสร้างคลาส Bison ของแอฟริกาโดยขยายคลาส HeavyAnimals

ดังนั้นตอนนี้คุณเป็นผู้ปกครอง (สัตว์) ซึ่งสามารถใช้ในการสร้างคลาสฐานอื่นเช่นคลาส HeavyAnimal ด้วยสามารถใช้ในการสร้างคลาสกระทิงกระทิงแอฟริกาและสัตว์หนักอื่น ๆ ดังนั้นด้วย Bison แอฟริกาตอนนี้คุณสามารถเข้าถึงวิธีการและคุณสมบัติของ Animal Class (นี่คือพื้นฐานสำหรับสัตว์ทั้งหมด) และการเข้าถึงคลาส HeavyAnimals (นี่คือพื้นฐานสำหรับสัตว์หนัก)


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