สแต็กขยาย LinkedList การละเมิดหลักการทดแทน Liskov หรือไม่?


13

คลาส LinkedList มีอยู่พร้อมกับฟังก์ชั่นเช่น add_first (), add_last (), add_after (), remove_first (), remove_last () และ remove ()

ขณะนี้มีคลาสสแต็กที่จัดเตรียมฟังก์ชันการใช้งานเช่น push (), pop (), peek () หรือ top () และเพื่อใช้เมธอดเหล่านี้จะขยายเมธอดคลาส LinkedList นี่เป็นการละเมิดหลักการทดแทนของ Liskov หรือไม่?

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

นอกจากนี้หากไม่เป็นการละเมิดการออกแบบที่ไม่ดีนี้เป็นอย่างไร และคุณจะใช้ฟังก์ชั่น Stack โดยใช้คลาส LinkedList ได้อย่างไร


6
คำถามอะไรคือข้อเสียของการกำหนดคลาสเป็นคลาสย่อยของรายการของตัวเอง? ถามเกี่ยวกับปัญหาที่แตกต่างกันเล็กน้อย แต่คำตอบส่วนใหญ่จะใช้ที่นี่เช่นกัน: คุณควรสร้างกองซ้อนที่มีรายชื่อเป็นสมาชิกส่วนตัวแทนการสืบทอดจากรายการ
amon

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

2
คุณต้องการที่จะขยายLinkedListหรือไม่ คุณอาจต้องการฟังคำแนะนำของ Joshua Bloch (ผู้ที่มีบทบาทสำคัญในการออกแบบเฟรมเวิร์กคอลเลกชัน Java) เมื่อเขาพูดว่า "ชอบองค์ประกอบมากกว่าการสืบทอด" - อาจมีLinkedListคลาสสแต็กของคุณอยู่บ้าง
corsiKa

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

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

คำตอบ:


31

ขณะนี้มีคลาสสแต็กที่จัดเตรียมฟังก์ชันการใช้งานเช่น push (), pop (), peek () หรือ top () และเพื่อใช้เมธอดเหล่านี้จะขยายเมธอดคลาส LinkedList นี่เป็นการละเมิดหลักการทดแทนของ Liskov หรือไม่?

ไม่มันเป็นการดีที่จะเพิ่มวิธีการในประเภทย่อย

ตัวอย่างเช่นพิจารณา case add_after () เพื่อเพิ่มโหนดที่ตำแหน่งที่ n ของรายการที่เชื่อมโยง สามารถทำได้ในคลาสพื้นฐาน แต่ไม่สามารถอยู่ในคลาสสแต็กได้

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

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

การปรับเปลี่ยนadd_after()วิธีการในลักษณะนี้เป็นการละเมิดกฎประวัติศาสตร์ (กฎที่สำคัญที่สุด!) และดังนั้นจึงไม่ได้ช่วยแก้ไขการละเมิด LSP

และคุณจะใช้ฟังก์ชั่น Stack โดยใช้คลาส LinkedList ได้อย่างไร

โดยใช้องค์ประกอบ

หมายเหตุ: ทุกอย่างที่ผมเขียนข้างต้นเท่านั้นที่นำไปใช้กับภาษาที่สับสน Subtyping และ subclassing! LSP เกี่ยวกับการพิมพ์ย่อยไม่ใช่การทำคลาสย่อย ในภาษาที่ไม่สับสนทั้งสองก็จะเป็นที่ยอมรับได้อย่างสมบูรณ์แบบที่จะทำให้Stacksubclass ของLinkedListตราบใดที่คุณไม่ได้ทำให้มันเป็นชนิดย่อยLinkedListของ


9
กฎประวัติศาสตร์คืออะไร ฉันหามันไม่เจอในอินเทอร์เน็ต
Zapadlo

10
เมื่อจัดการกับวัตถุของชนิดย่อยและสังเกตผ่านวิธีการของ supertype มันจะต้องเป็นไปไม่ได้ที่จะสังเกตเห็นประวัติศาสตร์ที่ไม่สามารถสังเกตเห็นได้ด้วยวัตถุของ supertype กฎนี้เป็นกฎที่ทำให้ LSP สามารถใช้กับภาษาที่ไม่สามารถใช้งานได้ สิ่งอื่น ๆ ทั้งหมดเป็นที่รู้จักกันมานาน กฎก่อนและหลังมีการปฏิรูปกฎความร่วมมือและความขัดแย้งสำหรับประเภทฟังก์ชั่น กฎประวัติทำให้ทั้งหมดนี้ใช้กับข้อมูลที่ไม่แน่นอน หากไม่มีมัน LSP จะเป็นประโยชน์สำหรับภาษาที่ใช้งานได้จริงเท่านั้น
Jörg W Mittag

2
ฉันคิดว่าคำอธิบายในบทความ Wikipedia นั้นดีพอสมควร: wikipedia.org/wiki/Liskov_substitution_principleแต่เช่นเคยคุณควรอ่านต้นฉบับต้นฉบับจริงๆ
Jörg W Mittag

@ JörgWMittag: ในขณะที่ฉันคิดว่ามันมีเหตุผลสำหรับประเภทที่จะรวมอยู่ในสัญญาของพวกเขารับประกันเกี่ยวกับสิ่งที่ "ประวัติศาสตร์" จะหรือไม่ได้รับการสังเกตฉันไม่คิดว่ามันควรจะจำเป็นที่ supertype ทุกคนสามารถผลิตลำดับการสังเกตได้ทุกประเภท ที่อาจเป็นไปได้ในวัตถุของประเภทย่อย - เพียงว่าเอกสารประเภทความเป็นไปได้ที่ผู้บริโภคของประเภทอาจสังเกตเห็นลำดับดังกล่าว
supercat

1

เนื่องจากทุกอย่างได้รับการพูดถึงแล้วโดยJörg W Mittag ฉันจึงอธิบายรายละเอียดเล็กน้อยในส่วนต่อไปนี้:

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

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

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