2012-05-28 35 views
13

Tôi đang ở trong một tình huống rất giống với những gì Steve McConnell's trong Hoàn thành mã đã đề cập. Chỉ có vấn đề của tôi là dựa trên Xe cộ và Trike xảy ra là do luật pháp rơi vào hạng mục Ô tô. Ô tô có bốn bánh cho đến bây giờ. Bất kỳ cách nào tên miền của tôi là không cần thiết phức tạp vì vậy nó rất dễ dàng để dính với mèo ví dụ dưới đây.Làm cách nào để tránh vi phạm Nguyên tắc Thay thế Liskov (LSP)?

Hãy nghi ngờ của các lớp học đó ghi đè lên một thói quen và không làm gì cả bên trong thói quen bắt nguồn này thường chỉ ra một lỗi trong thiết kế của các lớp cơ sở. Ví dụ, giả sử bạn có một lớp Cat và Scratch() thường xuyên và giả sử rằng cuối cùng bạn phát hiện ra rằng một số mèo đã bị khai thác và không thể làm xước. Bạn có thể bị cám dỗ để tạo một lớp học bắt nguồn từ Mèo có tên ScratchlessCat và ghi đè lên thủ tục Scratch() để không làm gì cả. Cách tiếp cận này trình bày một số vấn đề:

Vi phạm sự trừu tượng (giao diện hợp đồng) được trình bày trong lớp Cát bằng cách thay đổi ngữ nghĩa giao diện của nó.

Cách tiếp cận này nhanh chóng mất kiểm soát khi bạn mở rộng nó sang các lớp bắt nguồn khác . Điều gì sẽ xảy ra khi bạn tìm thấy một con mèo không có đuôi? Hay con mèo không bắt chuột? Hay một con mèo không uống sữa? Cuối cùng bạn sẽ kết thúc với các lớp học có nguồn gốc như ScratchlessTaillessMicelessMilklessCat.

Theo thời gian, cách tiếp cận này dẫn đến mã gây nhầm lẫn cho duy trì vì các giao diện và hành vi của lớp tổ tiên ngụ ý rất ít hoặc không có gì về hành vi của hậu duệ của chúng.

Nơi khắc phục vấn đề này không nằm trong lớp cơ sở, nhưng trong lớp học ban đầu là Cat. Tạo một lớp Claws và chứa nó trong lớp học Cats. Vấn đề gốc là giả định rằng tất cả các con mèo đầu, để khắc phục vấn đề đó tại nguồn, thay vì chỉ băng bó nó tại đích.

Theo văn bản từ cuốn sách tuyệt vời của ông ở trên. Sau đây là xấu

tầng lớp phụ huynh không phải là trừu tượng

public abstract class Cat { 
    public void scratch() { 
     System.out.println("I can scratch"); 
    } 
} 

nguồn gốc lớp

public class ScratchlessCat extends Cat { 
    @Override 
    public void scratch() { 
     // do nothing 
    } 
} 

Giờ đây, ông gợi ý tạo ra một lớp Claws, nhưng tôi không hiểu làm thế nào có thể Tôi sử dụng lớp này để tránh sự cần thiết cho ScratchlessCat#Scratch.

+2

Ví dụ về mèo là minh họa tốt về LSP. Trong việc tìm hiểu về LSP, tôi đã ngạc nhiên trước bao nhiêu ví dụ nghèo nàn và có bao nhiêu sự hiểu lầm trên SO và các trang web khác. – SteveT

Trả lời

8

Bạn vẫn sẽ có một phương pháp scratch(), nhưng nó sẽ không được ghi đè bởi các lớp thừa kế:

public class Cat { 
    Claw claw_; 
    public Cat(Claw claw) {claw = claw_;} 
    public final void scratch() { 
    if (claw_ != null) { 
     claw_.scratch(this); 
    } 
    } 
} 

này cho phép bạn ủy logic gãi đến chứa Claw đối tượng, nếu có (và không trầy xước nếu không có móng vuốt). Các lớp học có nguồn gốc từ mèo không có tiếng nói trong vấn đề làm thế nào để đầu, vì vậy không cần phải tạo ra hệ thống phân cấp bóng dựa trên khả năng.

Ngoài ra, vì các lớp dẫn xuất không thể thay đổi việc triển khai phương thức, không có vấn đề gì trong chúng phá vỡ ngữ nghĩa dự định của phương thức scratch() trong giao diện của lớp cơ sở.

Nếu bạn thực hiện điều này đến mức cực đại, bạn có thể thấy rằng bạn có nhiều lớp và không có nhiều dẫn xuất - hầu hết logic được giao cho các đối tượng sáng tác, không giao phó cho các lớp dẫn xuất.

11

Không phải tất cả mèo đều có móng vuốt và có khả năng gãi là một đầu mối lớn mà Cat không được xác định phương thức công khai scratch trong API của nó. Bước đầu tiên là xem xét lý do tại sao bạn xác định scratch ở địa điểm đầu tiên. Có lẽ mèo gãi nếu chúng có thể bị tấn công; nếu họ không rít lên hay bỏ chạy.

public class Cat extends Animal { 
    private Claws claws; 

    public void onAttacked(Animal attacker) { 
     if (claws != null) { 
      claws.scratch(attacker); 
     } 
     else { 
      // Assumes all cats can hiss. 
      // This may also be untrue and you need Voicebox. 
      // Rinse and repeat. 
      hiss(); 
     } 
    } 
} 

Bây giờ bạn có thể thay thế bất kỳ Cat lớp con cho người khác và nó sẽ hoạt động một cách chính xác tùy thuộc vào việc hay không nó có móng vuốt. Bạn có thể xác định lớp học DefenseMechanism để tổ chức các phòng thủ khác nhau như Scratch, Hiss, Bite, v.v.

Các vấn đề liên quan