Nguyên tắc thay thế của Liskov: Nếu tiểu loại có một số hành vi bổ sung được thực hiện, không có trong loại, thì đây có phải là vi phạm LSP không?


8

Trong hành trình viết mã tốt hơn, sạch hơn, tôi đang tìm hiểu về các nguyên tắc RẮN. Trong đó, LSP đang tỏ ra hơi khó nắm bắt đúng cách.

Sự nghi ngờ của tôi là điều gì xảy ra nếu tôi có một số phương thức bổ sung trong tiểu loại của tôi, S, không có trong loại, T, liệu điều này có luôn vi phạm LSP không? Nếu có, làm thế nào để extendtôi lớp học của tôi?

Ví dụ: giả sử chúng ta có một Birdloại. Và các kiểu con của nó là EagleHumming Bird. Bây giờ cả hai kiểu con có một số hành vi phổ biến là Bird. Nhưng Eaglecũng có hành vi săn mồi tốt (không có trong Birdloại chung ), mà tôi muốn sử dụng . Do đó, bây giờ tôi sẽ không thể làm điều này:

Bird bird = new Eagle();

Vì vậy, việc đưa ra Eaglenhững hành vi bổ sung phá vỡ LSP?

Nếu có, điều đó có nghĩa là tôi không thể mở rộng các lớp học vì điều đó sẽ gây ra vi phạm LSP?

class Eagle extends Bird {
   //we are extending Bird since Eagle has some extra behavior also
}

Mở rộng các lớp nên được cho phép theo nguyên tắc Mở / Đóng phải không?

Cảm ơn bạn đã trả lời trước! Như bạn có thể thấy rõ, LSP khiến tôi bối rối như mọi thứ.

Chỉnh sửa: Tham khảo câu trả lời SO này. Trong trường hợp này một lần nữa, khi Carcó hành vi bổ sung như thế ChangeGear, nó vi phạm LSP. Vậy, làm thế nào để chúng tôi mở rộng một lớp học mà không vi phạm LSP?



Tôi đã trải qua điều đó, nhưng nó không trả lời câu hỏi của tôi. Tôi đã đọc rất nhiều câu trả lời thực sự, nhưng cho đến nay không có sự giúp đỡ nào.
user270386

1
Đây là câu trả lời đúng nhất, bạn đã đọc nó chưa Nếu không, hãy suy nghĩ lại về thiết kế của bạn.
gnat

@gnat, Vâng tôi đã làm, nhưng tôi hơi chậm :) Và tôi thường cần giải thích nhiều hơn những người khác có thể yêu cầu. Sau khi đọc câu trả lời thấu đáo của David Arno, tôi có thể liên quan đến dòng đó ngay bây giờ.
user270386

1
@FrankHileman nhiều người trong chúng ta làm việc với trình biên dịch và cơ sở mã ít hơn lý tưởng. Ngay cả khi chúng ta không làm điều đó vẫn là một điều tốt khi con người cũng hiểu cách tôn trọng họ.
candied_orange

Câu trả lời:


10

Sự nghi ngờ của tôi là điều gì xảy ra nếu tôi có một số phương thức bổ sung trong tiểu loại của tôi, S, không có trong loại, T, liệu điều này có luôn vi phạm LSP không?

Câu trả lời rất đơn giản: không.

Điểm quan trọng đối với LSP là Snên thay thế cho T. Vì vậy, nếu Tthực hiện một deletechức năng, Scũng nên thực hiện nó và nên thực hiện xóa khi được gọi. Tuy nhiên, Smiễn phí để thêm chức năng bổ sung hơn và những gì Tcung cấp. Người tiêu dùng của a T, khi được cung cấp Ssẽ không biết về chức năng bổ sung này, nhưng nó được phép tồn tại cho người tiêu dùng Strực tiếp sử dụng.

Rất nhiều ví dụ về cách nguyên tắc có thể bị vi phạm có thể là:

class T
{
    bool delete(Item x)
    {
        if (item exists)
        {
            delete it
            return true;
        }
        return false;
    }
}

class S extends T
{
    bool delete(Item x)
    {
        if (item doesn't exist)
        {
            add it
            return false;
        }
        return true;
    }
}

Câu trả lời phức tạp hơn một chút: không, miễn là bạn không bắt đầu ảnh hưởng đến trạng thái hoặc hành vi dự kiến ​​khác của loại cơ sở.

Ví dụ: những điều sau đây sẽ là vi phạm:

class Point2D
{
    private readonly double _x;
    private readonly double _y;

    public virtual double X => _x;
    public virtual double Y => _y;

    public Point2D(double x, double y) => (_x, _y) = (x, y);
}

class MyPoint2D : Point2D
{
    private double _x;
    private double _y;

    public override double X => _x;
    public override double Y => _y;

    public MyPoint2D(double x, double y) : 
        base(x, y) => (_x, _y) = (x, y);

    public void Update(double x, double y) => (_x, _y) = (x, y);
}

Các loại Point2D, là bất biến; trạng thái của nó không thể thay đổi. Với MyPoint2D, tôi đã cố tình lách hành vi đó để biến nó thành đột biến. Điều đó phá vỡ các ràng buộc lịch sử Point2Dvà do đó là vi phạm LSP.


Có lẽ một bổ sung tốt cho câu trả lời này sẽ là một ví dụ về hành vi có thể được thêm vào một deletechức năng như vậy sẽ là vi phạm LSP?
Andy Hunt

1
@ user270386: Không. LSP không phải là về cấu trúc của lớp con, nó là về hành vi của mã lớp con, so với hành vi của lớp cơ sở. Bản thân nó không phải là chức năng bổ sung vi phạm ràng buộc lịch sử; thay vào đó, ràng buộc bị vi phạm nếu chức năng mới này thực hiện điều gì đó bất ngờ (ví dụ bị cấm) trong lớp cơ sở (và điều này bao gồm những điều có thể được thể hiện qua ngôn ngữ, cũng như những điều chỉ có thể được thể hiện thông qua tài liệu).
Filip Milovanović

1
Có thể đáng chú ý rằng nếu T là một giao diện nghiêm ngặt, lớp trừu tượng hoàn toàn hoặc thậm chí là mẫu đối tượng null thì S có khá nhiều về cấu trúc. T là mã. Đây không nhất thiết là thông số kỹ thuật, tài liệu yêu cầu hoặc chủ sở hữu sản phẩm. Chúng tôi chỉ quan tâm đến các vi phạm LSP khi T đang được sử dụng để thể hiện một ràng buộc phải luôn luôn giữ. Không phải lớp nào cũng làm được điều đó. Nhưng nếu bạn bảo S xóa và theo thiết kế thì S không có gì tốt hơn với phần còn lại của mã. Không thể cho bạn biết nếu đó là sự thật. Nó chỉ có thể cho bạn biết rằng bạn nên kiểm tra tốt hơn trước khi bạn làm điều này.
candied_orange

1
(còn tiếp) IMO, đó là yêu cầu về khả năng thay thế thúc đẩy tất cả điều này. Sự thay thế thực sự bắt nguồn từ khả năng đối xử với hai cách thực hiện (hành vi) khác nhau tương đương ở một mức độ trừu tượng cao hơn được quy định bởi hợp đồng được xác định bởi siêu kiểu. Cấu trúc, theo nghĩa đó, là một phương tiện để kết thúc.
Filip Milovanović

1
@DavidArno "mã của loại cơ sở" - không phải là thông số kỹ thuật, cũng không nhất thiết phải truy cập được.
Frank Hileman

3

Dĩ nhiên là không. Nếu đối tượng Eagle có thể được sử dụng bởi bất kỳ mã nào mong đợi Bird hoặc lớp con và hành xử như một Bird sẽ hành xử, bạn vẫn ổn.

Tất nhiên hành vi Eagle chỉ có thể được sử dụng bởi mã nhận thức được rằng đó là một đối tượng như vậy. Chúng tôi hy vọng rằng một số mã sẽ rõ ràng tạo ra một đối tượng Eagle và sử dụng nó như một đối tượng Eagle, trong khi có thể sử dụng bất kỳ mã nào mong đợi các đối tượng Bird.

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.