คลาสควรรู้เกี่ยวกับคลาสย่อยหรือไม่?


24

คลาสควรรู้เกี่ยวกับคลาสย่อยหรือไม่? คลาสควรทำสิ่งที่เฉพาะเจาะจงสำหรับคลาสย่อยที่กำหนดเช่น?

สัญชาตญาณของฉันบอกฉันว่ามันเป็นการออกแบบที่ไม่ดีดูเหมือนว่าจะมีรูปแบบต่อต้านบางอย่าง


6
ปกติแล้วจะไม่มี แต่ในกรณีพิเศษมันมีประโยชน์
CodesInChaos

มันคือการต่อต้านรูปแบบที่เป็นที่รู้จักกันดีอย่างใดอย่างหนึ่งที่เรียกว่า "Liskov ชดเชยหลักการ" หรือบางครั้งก็ LSP อ่านเกี่ยวกับเรื่องที่en.wikipedia.org/wiki/Liskov_substitution_principle
จิมมี่ฮอฟฟา

5
@JimmyHoffa - "หลักการทดแทน Liskov" ไม่ใช่ชื่อของรูปแบบการต่อต้าน รูปแบบการต่อต้านอาจละเมิด LSP แต่ก็ไม่แน่ใจว่าสิ่งนี้จะเป็นจริงได้ที่นี่ มีความเป็นไปได้ที่แม้ว่าประเภทหนึ่งจะรู้ว่าเป็นชนิดย่อยก็ตาม แต่ชนิดย่อยนั้นยังสามารถทดแทนได้สำหรับผู้ปกครองด้วยความแปรปรวนและความแปรปรวนร่วม
Matthew Flynn

4
@ MatthewFlynn บางทีฉันใช้ LSP เพียงเล็กน้อยอย่างหลวม ๆ แต่ในคำจำกัดความของฉันมันไม่ได้เป็นความจริงที่ว่าพวกเขาทั้งหมดปฏิบัติตามข้อกำหนดของแต่ละคนที่เป็นไปตาม LSP เป็นข้อกำหนดที่พวกเขามี decoupling ดังกล่าวซึ่งไม่เพียง คุณสามารถใช้คลาสย่อยอื่นที่ไม่ละเมิด LSP หากลำดับชั้นนั้นทราบตนเองการดำเนินการภายนอกอาจไม่ตรงกับ LSP โดยไม่ต้องเปลี่ยนคลาสพื้นฐาน บางทีนั่นอาจไม่ใช่ LSP ที่เข้มงวด แต่มันใกล้เคียงอย่างยิ่ง และใช่ฉันควรจะกล่าวว่าการละเมิด LSP เป็นรูปแบบการต่อต้าน
จิมมี่ฮอฟฟา

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

คำตอบ:


32

คำตอบโดยนัยของแนวคิดของคลาสคือ "ไม่"

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

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


4
ข้อยกเว้นหนึ่งข้อสำหรับกฎนี้: เอกสารของคลาสควรแสดงรายการคลาสย่อยภายในเครื่องไปยังไลบรารีของตัวเอง
Kieveli

3
@Kieveli ... ตราบเท่าที่เอกสารดังกล่าวได้รับการปรับปรุงให้ทันสมัย
mouviciel

14
เครื่องมือใด ๆ เอกสารที่ใช้งานควรจะสามารถวาดต้นไม้มรดก ...
ฮันเนส

13
ฉันไม่คิดว่ามันเป็นข้อยกเว้น ชั้นเรียนยังไม่รู้อะไรเลย เอกสารประกอบรู้
Honza Brabec

4
@Kieveli เอ่อฉันไม่เห็นด้วยอย่างสมบูรณ์
จิมมี่ฮอฟฟา

16

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

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

ใน Scala สิ่งนี้ใช้เพื่อสร้างแบบจำลองข้อมูลพีชคณิตแบบปิดดังนั้นประเภท Canonical Haskell List:

data List a = Nil | Cons a (List a)

สามารถสร้างแบบจำลองใน Scala เช่นนี้:

sealed trait List[+A]

case object Nil extends List[Nothing]

final case class Cons[+A] extends List[A]

และคุณสามารถรับประกันได้ว่ามีเพียงคลาสย่อยสองคลาสที่มีอยู่เนื่องจากListเป็นsealedและไม่สามารถขยายนอกไฟล์Consได้finalดังนั้นจึงไม่สามารถขยายได้เลยและNilเป็นสิ่งobjectที่ไม่สามารถขยายได้

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


สิ่งที่ใช้ได้ผลในหลายภาษาคือการเพิ่มabstract internalสมาชิกบางรูปแบบ
CodesInChaos

4

คำตอบง่ายๆคือไม่

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

  • หลักการชดเชย Liskov
  • หลักการเปิดปิด

1

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

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


9
ไม่ไม่และไม่ มันไม่มีประโยชน์และไม่เคยมีทางออกที่ดีเลย
Sulthan

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

1
@JohnGaughan คำว่า "รู้" ทำให้ฉันสับสน Nodeแน่นอนว่าคลาสพื้นฐานนั้นไม่มีการอ้างอิงโดยตรงไปยังคลาสย่อย แต่มันมีacceptวิธีการที่มีVisitorพารามิเตอร์ และVisitorมีvisitวิธีการต่อแต่ละคลาสย่อย ดังนั้นแม้จะNodeไม่มีการอ้างอิงโดยตรงไปยังคลาสย่อยของมันมัน "รู้" เกี่ยวกับพวกเขาทางอ้อมผ่านVisitorอินเตอร์เฟซ พวกเขาทั้งหมดรวมเข้าด้วยกันผ่านมัน
lorus

1
@ Sulthan ไม่เคยพูดไม่เคย ประเภทตัวเลือกเป็นตัวอย่างที่ถูกต้องของเคสเมื่อ supreclass ทราบเกี่ยวกับการสืบทอด แต่อย่างที่ Jorg บอกว่ามันจะถูกทำเครื่องหมายว่าปิดผนึก
Pavel Voronin

จากนั้นจะเห็นด้วย: ผู้เข้าชมจะต้องรู้ว่ามันคืออะไรเยี่ยมชม

1

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

No!

เช่นเดียวกันกับคลาส เชื่อสัญชาตญาณของคุณ


งานรักษาความปลอดภัย???
sixtyfootersdude

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