Ngăn xếp mở rộng LinkedList. Vi phạm nguyên tắc thay thế Liskov?


13

Một lớp LinkedList tồn tại với các hàm như add_first (), add_last (), add_after (), remove_first (), remove_last () và remove ()

Bây giờ có một lớp Stack cung cấp các chức năng như push (), pop (), peek () hoặc top () và để thực hiện các phương thức này, nó mở rộng các phương thức lớp LinkedList. Đây có phải là vi phạm Nguyên tắc thay thế Liskov không?

Ví dụ, hãy xem xét trường hợp add_after () để thêm một nút ở vị trí thứ n của Danh sách được liên kết. Điều này có thể được thực hiện trong lớp cơ sở nhưng không phải trong lớp Stack. Các postconditions đang bị suy yếu ở đây hay bạn sửa đổi phương thức add_after () để thêm vào đầu Stack?

Ngoài ra, nếu không vi phạm, đây có phải là thiết kế xấu? Và bạn sẽ triển khai chức năng Stack bằng cách sử dụng lớp LinkedList như thế nào?


6
Câu hỏi Điều gì sẽ là bất lợi khi định nghĩa một lớp là một lớp con của một danh sách của chính nó? hỏi về một vấn đề hơi khác, nhưng hầu hết các câu trả lời cũng áp dụng ở đây: bạn nên tạo một Stack với Danh sách là thành viên riêng thay vì kế thừa từ Danh sách.
amon

7
Có, đây là một thiết kế tồi vì nó sẽ ngăn bạn thay đổi biểu diễn bên trong của ngăn xếp thành một thứ khác ngoài danh sách được liên kết (ví dụ: một mảng). Bạn cũng đang phơi bày các hoạt động mà ngăn xếp thường không hỗ trợ. Sử dụng thành phần thay vì thừa kế.
Lee

2
Bạn có muốn gia hạn LinkedListkhông? Bạn có thể muốn nghe theo lời khuyên của Joshua Bloch (người đóng vai trò chính trong việc thiết kế khung bộ sưu tập Java) khi anh ta nói "Thích sáng tác hơn thừa kế" - Có thể có một LinkedListlớp trong ngăn xếp của bạn, nhưng không thực sự mở rộng nó?
corsiKa

1
Tôi muốn nói rằng nó không thể đáp ứng nguyên tắc thay thế Liskov, bởi vì "ngăn xếp là một loại danh sách được liên kết" không phải là một tuyên bố đúng. "Stack" thực sự là một giao diện (ý tôi là về mặt khái niệm, không phải cấu trúc Java). Một ngăn xếp có thể được thực hiện với một danh sách liên kết. Giống như những người khác đã nói, bạn sẽ sử dụng một thành viên dữ liệu riêng tư thuộc loại LinkedListnặng trong hậu trường (bố cục). Bằng cách đó, người dùng mã của bạn không thể vô tình sử dụng Stack khi họ cần Danh sách hoặc ngược lại.
Jasmijn

1
Có vẻ như những gì sẽ được saner sẽ ngược lại. Nếu tôi đang làm điều này, có lẽ tôi sẽ có một lớp danh sách được liên kết thực hiện giao diện "ngăn xếp" vì nó hoàn toàn có khả năng làm điều đó. Mặt khác, đây là một cách sai lầm vì một ngăn xếp là một khái niệm, một danh sách được liên kết là một triển khai có thể hoạt động như một ngăn xếp trong số những thứ khác.
Vality

Câu trả lời:


31

Bây giờ có một lớp Stack cung cấp các chức năng như push (), pop (), peek () hoặc top () và để thực hiện các phương thức này, nó mở rộng các phương thức lớp LinkedList. Đây có phải là vi phạm Nguyên tắc thay thế Liskov không?

Không. Hoàn toàn tốt khi thêm các phương thức trong một kiểu con.

Ví dụ, hãy xem xét trường hợp add_after () để thêm một nút ở vị trí thứ n của Danh sách được liên kết. Điều này có thể được thực hiện trong lớp cơ sở nhưng không phải trong lớp Stack.

Đây vi phạm LSP. LSP nói rằng các thể hiện của một kiểu con phải được thay thế cho các thể hiện của siêu kiểu mà không thay đổi các thuộc tính mong muốn của một chương trình. Nếu một kiểu con loại bỏ một phương thức, thì bất kỳ mã nào gọi phương thức đó sẽ bị sập (hoặc có một NoMethodErrorngoại lệ hoặc một cái gì đó dọc theo các dòng đó). Rõ ràng, "không sụp đổ" là một tài sản mong muốn.

Các postconditions đang bị suy yếu ở đây hay bạn sửa đổi phương thức add_after () để thêm vào đầu Stack?

Sửa đổi add_after()phương thức theo cách này là vi phạm Quy tắc Lịch sử (quan trọng nhất trong các quy tắc!) Và do đó không giúp khắc phục vi phạm LSP.

Và bạn sẽ triển khai chức năng Stack bằng cách sử dụng lớp LinkedList như thế nào?

Bằng cách sử dụng thành phần.

LƯU Ý: Tất cả mọi thứ tôi viết ở trên chỉ áp dụng cho các ngôn ngữ gây nhầm lẫn giữa phân nhóm và phân lớp! LSP là về phân nhóm , không phân lớp. Trong một ngôn ngữ không gây nhầm lẫn cho cả hai, việc tạo Stackmột lớp con là hoàn toàn có thể chấp nhận được LinkedList, miễn là bạn không biến nó thành một kiểu con LinkedList.


9
Quy tắc lịch sử là gì? Tôi không tìm thấy nó trên internet.
Zapadlo

10
Khi thao túng một đối tượng của một kiểu con và quan sát nó thông qua các phương thức của siêu kiểu, không thể quan sát được một lịch sử không thể quan sát được với một đối tượng của siêu kiểu. Quy tắc này là một trong đó làm cho LSP áp dụng cho các ngôn ngữ phi chức năng. Tất cả những thứ khác đã được biết đến từ lâu trước đó. Các quy tắc trước và sau điều kiện là các cải cách của quy tắc đồng và chống đối với các loại chức năng. Quy tắc lịch sử làm cho tất cả điều này áp dụng cho dữ liệu có thể thay đổi. Không có nó, LSP sẽ chỉ hữu ích cho các ngôn ngữ hoàn toàn chức năng.
Jörg W Mittag

2
Tôi nghĩ rằng lời giải thích trong bài viết trên Wikipedia khá hợp lý: wikipedia.org/wiki/Liskov_substlation_principl Nhưng như mọi khi, bạn thực sự nên đọc nguồn gốc.
Jörg W Mittag

@ JörWWitt điều đó có thể xảy ra đối với một đối tượng của kiểu con - chỉ đơn thuần là siêu kiểu tài liệu có khả năng người tiêu dùng của siêu kiểu có thể quan sát các chuỗi như vậy.
supercat

1

Vì mọi thứ đã được giải quyết bởi Jörg W Mittag, tôi chỉ giải thích một chút về phần sau:

Các postconditions đang bị suy yếu ở đây hay bạn sửa đổi phương thức add_after () để thêm vào đầu Stack?

Về cơ bản, khi có một câu hỏi liệu một số hệ thống phân cấp có vi phạm LSP hay không, nó phụ thuộc vào hợp đồng bạn đặt ra. Vậy, hợp đồng nào add_aftercó? Tuy nhiên, nếu nó nghe có vẻ điên rồ, như "Thêm một nút ở vị trí thứ n hoặc ở trên cùng", thì bạn vẫn ổn, điều kiện hậu được đáp ứng, LSP không bị vi phạm. Nếu không, đó là vi phạm LSP.

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.