จะมีอะไรผิดพลาดได้หากหลักการชดเชย Liskov ถูกละเมิด?


27

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


6
มีอะไรผิดปกติถ้าคุณไม่ติดตาม LSP สถานการณ์ที่เลวร้ายที่สุด: คุณจบลงด้วยการเรียก Code-thulhu! ;)
FrustratedWithFormsDesigner

1
ในฐานะผู้เขียนคำถามต้นฉบับฉันต้องเพิ่มว่ามันเป็นคำถามเชิงวิชาการ แม้ว่าการละเมิดอาจทำให้เกิดข้อผิดพลาดในรหัสฉันไม่เคยมีข้อผิดพลาดร้ายแรงหรือปัญหาการบำรุงรักษาที่ฉันสามารถวางลงในการละเมิด LSP
Paul T Davies

2
@ พอลดังนั้นคุณไม่เคยมีปัญหากับโปรแกรมของคุณเนื่องจากลำดับขั้นของ OO ที่ซับซ้อน (ซึ่งคุณไม่ได้ออกแบบด้วยตัวเอง แต่อาจจะต้องขยายเวลา) ซึ่งสัญญาถูกแยกซ้ายและขวาโดยผู้ที่ไม่แน่ใจเกี่ยวกับจุดประสงค์ของชั้นฐาน เริ่มต้นกับ? ฉันอิจฉาคุณ! :)
Andres F.

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

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

คำตอบ:


31

ฉันคิดว่ามันพูดได้ดีมากในคำถามนั้นซึ่งเป็นหนึ่งในเหตุผลที่ได้รับการโหวตอย่างสูง

ตอนนี้เมื่อเรียก Close () บนงานมีโอกาสที่การโทรจะล้มเหลวหากเป็น ProjectTask ที่มีสถานะเริ่มต้นเมื่อไม่เป็นถ้าเป็นภารกิจพื้นฐาน

ลองนึกภาพถ้าคุณจะ:

public void ProcessTaskAndClose(Task taskToProcess)
{
    taskToProcess.Execute();
    taskToProcess.DateProcessed = DateTime.Now;
    taskToProcess.Close();
}

ในวิธีนี้บางครั้งการเรียก. ปิด () จะระเบิดดังนั้นตอนนี้ขึ้นอยู่กับการใช้งานจริงของประเภทที่ได้รับมาคุณต้องเปลี่ยนวิธีการทำงานของวิธีการนี้จากวิธีการเขียนวิธีนี้หากงานไม่มีประเภทย่อยที่อาจเป็น มอบให้กับวิธีการนี้

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


นั่นหมายความว่าชั้นเรียนเด็กไม่สามารถมีวิธีการสาธารณะของตัวเองที่ไม่ได้ประกาศในชั้นผู้ปกครองได้หรือไม่?
Songo

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

2
ไม่สิ่งนี้ใช้สำหรับเมื่อมีการอ้างอิงคลาสลูกราวกับว่าเป็นประเภทของคลาสพาเรนต์ซึ่งในกรณีนี้สมาชิกที่ไม่ได้ประกาศในคลาสพาเรนต์จะไม่สามารถเข้าถึงได้
Chewy Gumball

1
@Phil Yep; นี่คือคำจำกัดความของคลัปแน่น: การเปลี่ยนแปลงสิ่งหนึ่งทำให้เกิดการเปลี่ยนแปลงกับสิ่งอื่น คลาสแบบคู่ที่หลวมสามารถนำการเปลี่ยนแปลงมาใช้ได้โดยไม่ต้องให้คุณเปลี่ยนรหัสภายนอก นี่คือเหตุผลที่สัญญาเป็นสิ่งที่ดีพวกเขาแนะนำคุณเกี่ยวกับวิธีที่จะไม่เปลี่ยนแปลงผู้บริโภคในวัตถุของคุณ: ทำตามสัญญาและผู้บริโภคจะไม่ต้องการแก้ไขใด ๆ เมื่อผู้บริโภคของคุณจำเป็นต้องใช้รหัสในการดำเนินการของคุณมากกว่าสัญญาของคุณนี่คือการมีเพศสัมพันธ์อย่างแน่นหนาและจำเป็นเมื่อละเมิด LSP
จิมมี่ฮอฟฟา

1
@ user949300 ความสำเร็จของซอฟต์แวร์ชิ้นใดที่จะทำให้งานสำเร็จไม่ได้วัดจากคุณภาพระยะยาวหรือต้นทุนระยะสั้น หลักการออกแบบเป็นความพยายามในการนำแนวทางเกี่ยวกับการลดต้นทุนระยะยาวของซอฟต์แวร์ไม่ใช่เพื่อให้ซอฟต์แวร์ "ทำงาน" ผู้คนสามารถปฏิบัติตามหลักการทั้งหมดที่พวกเขาต้องการในขณะที่ยังไม่สามารถใช้งานโซลูชันหรือไม่ทำตามเลยและใช้งานโซลูชัน แม้ว่าคอลเลกชัน java อาจใช้งานได้สำหรับหลาย ๆ คน แต่นั่นไม่ได้หมายความว่าค่าใช้จ่ายในการทำงานกับพวกเขาในระยะยาวจะถูกเท่าที่ควร
Jimmy Hoffa

13

หากคุณไม่ปฏิบัติตามสัญญาที่กำหนดไว้ในคลาสพื้นฐานสิ่งต่าง ๆ อาจล้มเหลวได้อย่างเงียบ ๆ เมื่อคุณได้รับผลลัพธ์ที่ปิด

LSP ในรัฐวิกิพีเดีย

  • เงื่อนไขเบื้องต้นไม่สามารถเสริมความแข็งแกร่งในประเภทย่อย
  • Postconditions ไม่สามารถลดลงได้ในประเภทย่อย
  • ค่าคงที่ของซูเปอร์ไทป์จะต้องเก็บรักษาไว้ในประเภทย่อย

หากสิ่งเหล่านี้ไม่ถือผู้โทรอาจได้รับผลลัพธ์ที่เขาไม่คาดหวัง


1
คุณนึกถึงตัวอย่างที่เป็นรูปธรรมใด ๆ
Mark Booth

1
@ MarkBooth ปัญหาวงกลมวงรี / สี่เหลี่ยมจตุรัสอาจมีประโยชน์ในการแสดงให้เห็น; บทความวิกิพีเดียเป็นจุดเริ่มต้นที่ดี: en.wikipedia.org/wiki/Circle-ellipse_problem
Ed Hastings

7

พิจารณากรณีคลาสสิกจากบันทึกคำถามสัมภาษณ์: คุณได้รับ Circle จากวงรี ทำไม? เพราะวงรีคือวงรีแน่นอน!

ยกเว้น ... ellipse มีสองฟังก์ชัน:

Ellipse.set_alpha_radius(d)
Ellipse.set_beta_radius(d)

เห็นได้ชัดว่าสิ่งเหล่านี้จะต้องกำหนดใหม่สำหรับ Circle เพราะ Circle มีรัศมีสม่ำเสมอ คุณมีความเป็นไปได้สองอย่าง:

  1. หลังจากเรียก set_alpha_radius หรือ set_beta_radius ทั้งคู่จะถูกตั้งค่าเป็นจำนวนเดียวกัน
  2. หลังจากเรียก set_alpha_radius หรือ set_beta_radius วัตถุจะไม่เป็นวงกลมอีกต่อไป

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

some_function(Ellipse byref e)

ลองจินตนาการว่า some_function เรียก e.set_alpha_radius แต่เนื่องจาก e เป็นวงกลมจริงๆมันจึงมีรัศมีเบต้าที่น่าประหลาดใจเช่นกัน

และในที่นี้คือหลักการการทดแทน: คลาสย่อยต้องถูกแทนที่สำหรับซูเปอร์คลาส สิ่งที่น่าแปลกใจเกิดขึ้น


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

2
ในโลกที่ทำงานได้อย่างหมดจด (ด้วยวัตถุที่ไม่เปลี่ยนรูป) วิธีการ set_alpha_radius (d) จะมีผลตอบแทนประเภทวงรี (ทั้งในวงรีและในชั้นเรียนวงกลม)
Giorgio

@Giorgio ใช่ฉันควรจะกล่าวว่าปัญหานี้เกิดขึ้นกับวัตถุที่ไม่แน่นอน
Kaz Dragon

@KazDragon: ทำไมทุกคนจะแทนที่วงรีด้วยวัตถุวงกลมเมื่อเรารู้ว่าวงรีไม่ใช่วงกลม? หากมีคนทำเช่นนั้นพวกเขาไม่มีความเข้าใจที่ถูกต้องเกี่ยวกับสิ่งที่พวกเขากำลังพยายามทำตัวเป็นแบบอย่าง แต่ด้วยการอนุญาตให้มีการทดแทนนี้เราไม่สนับสนุนให้มีความเข้าใจที่ไม่ดีเกี่ยวกับระบบพื้นฐานที่เราพยายามทำตัวเป็นแบบจำลองในซอฟต์แวร์ของเรา
ไม่ฝักใฝ่ฝ่ายใด

@ ไม่เชื่อฉันเชื่อว่าคุณได้อ่านความสัมพันธ์ที่ฉันอธิบายไปแล้ว ข้อเสนอคือความสัมพันธ์คือวิธีอื่น ๆ : วงกลมเป็นวงรี โดยเฉพาะวงกลมเป็นวงรีที่รัศมีอัลฟาและเบต้าเหมือนกัน ดังนั้นความคาดหวังอาจเป็นได้ว่าฟังก์ชั่นใด ๆ ที่คาดหวังว่าวงรีเป็นพารามิเตอร์สามารถนำวงกลม พิจารณา calcul_area (วงรี) ผ่านวงกลมที่จะให้ผลลัพธ์เดียวกัน แต่ปัญหาคือพฤติกรรมของฟังก์ชันการกลายพันธุ์ของ Ellipse นั้นไม่สามารถทดแทนได้สำหรับผู้ที่อยู่ใน Circle
Kaz Dragon

6

ในคำพูดของคนธรรมดา:

รหัสของคุณจะมีมากอันยิ่งใหญ่ของCASE / สวิทช์ข้อทั่ว

กรณีของส่วนคำสั่ง CASE / switchเหล่านั้นจะต้องเพิ่มเคสใหม่เป็นระยะ ๆ ซึ่งหมายความว่าฐานของรหัสนั้นไม่สามารถปรับขนาดได้และบำรุงรักษาได้ตามที่ควรจะเป็น

LSP ช่วยให้โค้ดทำงานเหมือนฮาร์ดแวร์มากขึ้น:

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


2
-1: คำตอบที่ไม่ดี
Thomas Eding

3
@ โทมัสฉันไม่เห็นด้วย มันเป็นการเปรียบเทียบที่ดี เขาพูดเกี่ยวกับการไม่ทำลายความคาดหวังซึ่งเป็นเรื่องของ LSP (แม้ว่าส่วนเกี่ยวกับเคส / สวิตช์ค่อนข้างอ่อนแอ แต่ฉันเห็นด้วย)
Andres F.

2
จากนั้น Apple ก็แยก LSP ออกโดยการเปลี่ยนขั้วต่อ คำตอบนี้มีชีวิตอยู่
หมอผี

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

การลดลงอย่างมากของจำนวนงบสวิตช์เป็นผลข้างเคียงที่ต้องการของ LSP ในขณะที่วัตถุสามารถยืนสำหรับวัตถุอื่น ๆ ที่ขยายอินเทอร์เฟซเดียวกันไม่จำเป็นสำหรับกรณีพิเศษที่จะได้รับการดูแล
Tulains Córdova

1

เพื่อยกตัวอย่างชีวิตจริงด้วยUndoManager ของจาวา

มันสืบทอดมาจากAbstractUndoableEditสัญญาที่ระบุว่ามันมี 2 สถานะ (ยกเลิกและทำซ้ำ) และสามารถไประหว่างพวกเขาด้วยการโทรเพียงครั้งเดียวไปยังundo()และredo()

อย่างไรก็ตาม UndoManager มีสถานะมากกว่าและทำหน้าที่เหมือนเลิกทำบัฟเฟอร์ (การเรียกแต่ละครั้งเพื่อundoยกเลิกการแก้ไขบางส่วนแต่ไม่ใช่ทั้งหมดการแก้ไขทำให้อ่อนแอลงภายหลัง)

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

ฉันกลิ้งตัวเองUndoManagerเพื่อหลีกเลี่ยงปัญหานั้น (ฉันควรเปลี่ยนชื่อเป็นUndoBuffer)


1

ตัวอย่าง: คุณกำลังทำงานกับเฟรมเวิร์ก UI และคุณสร้างการควบคุม UI แบบกำหนดเองของคุณเองโดยการคลาสคลาสControlฐานย่อย Controlชั้นฐานกำหนดวิธีgetSubControls()ซึ่งควรจะกลับไปคอลเลกชันของการควบคุมที่ซ้อนกัน (ถ้ามี) แต่คุณแทนที่วิธีการที่จะส่งกลับรายการวันเกิดของประธานาธิบดีแห่งสหรัฐอเมริกา

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


0

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

ดังนั้นการละเมิด LSP หมายความว่ามีความขัดแย้งในการออกแบบของคุณ: คุณกำลังกำหนดหมวดหมู่บางอย่างสำหรับวัตถุของคุณและจากนั้นคุณจะไม่เคารพพวกเขาในการใช้งานของคุณบางสิ่งต้องผิด

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


0

ฉันเพิ่งสืบทอด codebase เมื่อเร็ว ๆ นี้ที่มีผู้ละเมิดหลัก Liskov อยู่ ในชั้นเรียนที่สำคัญ นี่ทำให้ฉันเจ็บปวดอย่างมาก ให้ฉันอธิบายว่าทำไม

ฉันมีที่มาจาก Class A และแบ่งปันคุณสมบัติมากมายที่แทนที่ด้วยการใช้งานของตนเอง การตั้งค่าหรือได้รับคุณสมบัติที่มีผลกระทบที่แตกต่างกันในการตั้งหรือการที่แน่นอนคุณสมบัติเดียวกันจากClass BClass AClass BClass AClass AClass B

public Class A
{
    public virtual string Name
    {
        get; set;
    }
}

Class B : A
{
    public override string Name
    {
        get
        {
            return TranslateName(base.Name);
        }
        set
        {
            base.Name = value;
            FunctionWithSideEffects();
        }
    }
}

นอกเหนือจากข้อเท็จจริงที่ว่านี่เป็นวิธีที่น่ากลัวที่สุดในการทำการแปลใน. NET มีปัญหาอื่น ๆ อีกมากมายที่เกี่ยวข้องกับรหัสนี้

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

การใช้รหัสวัตถุของทั้งสองClass AและClass Bดังนั้นฉันไม่สามารถเพียงแค่ทำให้นามธรรมที่จะบังคับให้คนที่จะใช้Class AClass B

มีบางฟังก์ชั่นยูทิลิตี้ที่มีประโยชน์มากที่ทำงานบนมีClass Aและฟังก์ชั่นยูทิลิตี้อื่น ๆ Class Bที่มีประโยชน์มากที่ทำงานใน นึกคิดฉันต้องการที่จะสามารถที่จะใช้ฟังก์ชั่นยูทิลิตี้ที่สามารถทำงานบนบนClass A Class Bฟังก์ชั่นหลายอย่างที่ใช้ a Class Bสามารถทำได้อย่างง่ายดายClass Aถ้ามันไม่ได้เป็นการละเมิด LSP

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

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

เนื่องจาก codebase ไม่มีการทดสอบหน่วยรอบ ๆ มันค่อนข้างใกล้เคียงกับสถานการณ์ที่อันตรายที่สุดที่นักพัฒนาสามารถเผชิญได้ หากฉันไม่เปลี่ยนการละเมิดฉันต้องใช้พลังงานจำนวนมากในการติดตามวัตถุประเภทใดที่กำลังดำเนินการในแต่ละวิธีและหากฉันแก้ไขการละเมิดฉันสามารถทำให้ผลิตภัณฑ์ทั้งหมดระเบิดในเวลาที่ไม่เหมาะสม


จะเกิดอะไรขึ้นถ้าภายในคลาสที่ได้รับคุณสร้างเงาที่สืบทอดคุณสมบัติด้วยสิ่งต่าง ๆ ที่มีชื่อเดียวกัน [เช่นคลาสที่ซ้อนกัน] และสร้างตัวระบุใหม่BaseNameและTranslatedNameเข้าถึงทั้งสไตล์คลาส A Nameและความหมายคลาส B จากนั้นความพยายามในการเข้าถึงNameตัวแปรชนิดใด ๆBจะถูกปฏิเสธด้วยข้อผิดพลาดของคอมไพเลอร์ดังนั้นคุณสามารถมั่นใจได้ว่าการอ้างอิงทั้งหมดได้ถูกแปลงเป็นรูปแบบอื่นอย่างใดอย่างหนึ่ง
supercat

ฉันไม่ทำงานที่นั่นอีกแล้ว คงจะลำบากมากที่จะแก้ไข :-)
Stephen

-4

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


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