Tôi có hiểu chính xác rằng Nguyên tắc thay thế Liskov không thể được quan sát bằng các ngôn ngữ nơi các đối tượng có thể tự kiểm tra, giống như những gì thường thấy trong các ngôn ngữ gõ vịt?
Ví dụ, trong Ruby, nếu một lớp B
kế thừa từ một lớp A
, thì với mọi đối tượng x
của A
, x.class
sẽ quay trở lại A
, nhưng nếu x
là một đối tượng của B
, x.class
sẽ không quay trở lại A
.
Đây là một tuyên bố của LSP:
Hãy q (x) là một chứng minh tài sản về đối tượng x kiểu T . Sau đó q (y) nên chứng minh cho các đối tượng y kiểu S nơi S là một subtype của T .
Vì vậy, trong Ruby, ví dụ,
class T; end
class S < T; end
vi phạm LSP dưới hình thức này, như được chứng kiến bởi tài sản q (x) =x.class.name == 'T'
Thêm vào. Nếu câu trả lời là "có" (LSP không tương thích với hướng nội), thì câu hỏi khác của tôi sẽ là: có một dạng LSP "yếu" được sửa đổi nào có thể giữ cho ngôn ngữ động, có thể trong một số điều kiện bổ sung và chỉ với các loại đặc biệt của tài sản .
Cập nhật. Để tham khảo, đây là một công thức LSP khác mà tôi đã tìm thấy trên web:
Các hàm sử dụng các con trỏ hoặc tham chiếu đến các lớp cơ sở phải có thể sử dụng các đối tượng của các lớp dẫn xuất mà không biết nó.
Và một cái khác:
Nếu S là một kiểu con được khai báo của T, các đối tượng thuộc loại S sẽ hành xử như các đối tượng của loại T được dự kiến sẽ hành xử, nếu chúng được coi là các đối tượng của loại T.
Cái cuối cùng được chú thích với:
Lưu ý rằng LSP là tất cả về hành vi dự kiến của các đối tượng. Người ta chỉ có thể theo LSP nếu người ta biết rõ hành vi dự kiến của các đối tượng là gì.
Điều này dường như yếu hơn so với bản gốc và có thể quan sát được, nhưng tôi muốn thấy nó được chính thức hóa, đặc biệt giải thích ai là người quyết định hành vi dự kiến là gì.
Có phải LSP không phải là một thuộc tính của một cặp lớp trong ngôn ngữ lập trình, mà là một cặp lớp cùng với một tập các thuộc tính nhất định, được thỏa mãn bởi lớp tổ tiên? Trên thực tế, điều này có nghĩa là để xây dựng một lớp con (lớp con cháu) tôn trọng LSP, tất cả các cách sử dụng có thể có của lớp tổ tiên phải được biết đến? Theo LSP, lớp tổ tiên được cho là có thể thay thế với bất kỳ lớp con cháu nào, phải không?
Cập nhật. Tôi đã chấp nhận câu trả lời, nhưng tôi muốn thêm một ví dụ cụ thể từ Ruby để minh họa cho câu hỏi. Trong Ruby, mỗi lớp là một mô-đun theo nghĩa là Class
lớp là hậu duệ của Module
lớp. Tuy nhiên:
class C; end
C.is_a?(Module) # => true
C.class # => Class
Class.superclass # => Module
module M; end
M.class # => Module
o = Object.new
o.extend(M) # ok
o.extend(C) # => TypeError: wrong argument type Class (expected Module)