Làm thế nào để xác minh nguyên tắc thay thế Liskov trong hệ thống phân cấp thừa kế?


14

Lấy cảm hứng từ này trả lời:

Nguyên tắc thay thế Liskov yêu cầu rằng

  • Điều kiện tiên quyết không thể được tăng cường trong một kiểu con.
  • Postconditions không thể được làm yếu trong một kiểu con.
  • Bất biến của siêu kiểu phải được bảo toàn trong một kiểu con.
  • Ràng buộc lịch sử ("quy tắc lịch sử"). Các đối tượng được coi là có thể sửa đổi chỉ thông qua các phương thức của chúng (đóng gói). Vì các kiểu con có thể giới thiệu các phương thức không có trong siêu kiểu, nên việc giới thiệu các phương thức này có thể cho phép thay đổi trạng thái trong kiểu con không được phép trong siêu kiểu. Các ràng buộc lịch sử cấm điều này.

Tôi đã hy vọng nếu ai đó đăng một hệ thống phân cấp lớp vi phạm 4 điểm này và làm thế nào để giải quyết chúng cho phù hợp.
Tôi đang tìm kiếm một lời giải thích công phu cho các mục đích giáo dục về cách xác định từng điểm trong 4 điểm trong hệ thống phân cấp và cách tốt nhất để khắc phục nó.

Lưu ý:
Tôi đã hy vọng đăng một mẫu mã để mọi người làm việc, nhưng bản thân câu hỏi là về cách xác định hệ thống phân cấp bị lỗi :)


Có một số ví dụ khác về vi phạm LSP trong câu trả lời cho câu hỏi SO này
StuartLC

Câu trả lời:


17

Nó đơn giản hơn nhiều so với trích dẫn đó làm cho âm thanh, chính xác như nó là.

Khi bạn nhìn vào một hệ thống phân cấp thừa kế, hãy tưởng tượng một phương thức nhận một đối tượng của lớp cơ sở. Bây giờ hãy tự hỏi, có bất kỳ giả định nào mà ai đó chỉnh sửa phương thức này có thể làm cho nó không hợp lệ cho lớp đó không.

Ví dụ: ( ban đầu được nhìn thấy trên trang web của chú Bob ):

public class Square : Rectangle
{
    public Square(double width) : base(width, width)
    {
    }

    public override double Width
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Width;
        }
    }

    public override double Height
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Height;
        }
    }
}

Có vẻ đủ công bằng, phải không? Tôi đã tạo ra một loại Hình chữ nhật chuyên dụng gọi là Hình vuông, duy trì Chiều rộng phải bằng Chiều cao mọi lúc. Hình vuông là một hình chữ nhật, vì vậy nó phù hợp với các nguyên tắc OO, phải không?

Nhưng chờ đã, nếu bây giờ có ai đó viết phương pháp này:

public void Enlarge(Rectangle rect, double factor)
{
    rect.Width *= factor;
    rect.Height *= factor;
}

Không mát mẻ. Nhưng không có lý do gì mà tác giả của phương pháp này nên biết có thể có một vấn đề tiềm ẩn.

Mỗi khi bạn lấy được một lớp từ một lớp khác, hãy nghĩ về lớp cơ sở và những gì mọi người có thể giả định về nó (chẳng hạn như "nó có Chiều rộng và Chiều cao và cả hai sẽ độc lập"). Sau đó nghĩ "những giả định đó có còn hiệu lực trong lớp con của tôi không?" Nếu không, hãy suy nghĩ lại về thiết kế của bạn.


Ví dụ rất tốt và tinh tế. +1. Những gì bạn có thể làm là làm cho Phóng to một phương thức của lớp Hình chữ nhật và ghi đè lên nó trong lớp Square.
marco-fiset

@ marco-fiset: Tôi thà thấy Quảng trường và Hình chữ nhật tách rời, Hình vuông chỉ có một chiều, nhưng mỗi chiều thực hiện IResizable. Đúng là nếu có một phương thức Draw, chúng sẽ tương tự nhau, nhưng sau đó tôi muốn cả hai đóng gói một lớp hình chữ nhật, bao gồm mã chung.
pdr

1
Tôi không nghĩ rằng đây là một ví dụ tốt. Vấn đề là một hình vuông không có chiều rộng hoặc chiều cao. Nó chỉ có một chiều dài của nó. Vấn đề sẽ không có nếu chiều rộng và chiều cao chỉ có thể đọc được, nhưng chúng có thể ghi được trong trường hợp này. Khi giới thiệu trạng thái có thể sửa đổi, việc duy trì LSP luôn khó khăn hơn rất nhiều.
SpaceTrucker

@pdr Cảm ơn ví dụ, nhưng liên quan đến 4 điều kiện tôi đã đề cập trong bài đăng của mình, phần nào của Squarelớp vi phạm chúng?
Songo

1
@Songo: Đó là ràng buộc lịch sử. Giải thích rõ hơn ở đây: blackwasp.co.uk/LSP.aspx "Về bản chất, các lớp con bao gồm tất cả các phương thức và thuộc tính của siêu lớp của chúng. Chúng cũng có thể thêm các thành viên khác. Ràng buộc lịch sử nói rằng các thành viên mới hoặc đã sửa đổi không nên sửa đổi trạng thái của một đối tượng theo cách mà lớp cơ sở không cho phép . Ví dụ, nếu lớp cơ sở đại diện cho một đối tượng có kích thước cố định, lớp con không được phép thay đổi kích thước này. "
pdr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.