Điều kinh khủng đối với bộ nhớ đối số của người Viking là hoàn toàn sai, nhưng đó là một cách thực hành khách quan của người Bỉ. Khi bạn kế thừa từ một lớp, bạn không chỉ kế thừa các trường và phương thức bạn quan tâm. Thay vào đó, bạn kế thừa mọi thứ . Mọi phương pháp mà nó tuyên bố, ngay cả khi nó không hữu ích cho bạn. Và quan trọng nhất, bạn cũng kế thừa tất cả các hợp đồng của nó và đảm bảo rằng lớp cung cấp.
Từ viết tắt RẮN cung cấp một số phương pháp phỏng đoán cho thiết kế hướng đối tượng tốt. Ở đây, Nguyên tắc phân chia I nterface (ISP) và L iskov thay thế Bảng giá (LSP) có một cái gì đó để nói.
ISP bảo chúng tôi giữ cho giao diện của chúng tôi nhỏ nhất có thể. Nhưng bằng cách kế thừa từ ArrayList, bạn có được nhiều, nhiều phương pháp. Là nó có ý nghĩa để get(), remove(), set()(thay thế), hoặc add()(insert) một nút con tại một chỉ số cụ thể không? Có hợp lý với ensureCapacity()danh sách cơ bản không? Nó có nghĩa gì với sort()một nút? Là người dùng của lớp học của bạn thực sự cần phải có được một subList()? Vì bạn không thể ẩn các phương thức mà bạn không muốn, giải pháp duy nhất là có ArrayList làm biến thành viên và chuyển tiếp tất cả các phương thức mà bạn thực sự muốn:
private final ArrayList<Node> children = new ArrayList();
public void add(Node child) { children.add(child); }
public Iterator<Node> iterator() { return children.iterator(); }
Nếu bạn thực sự muốn tất cả các phương pháp bạn thấy trong tài liệu, chúng tôi có thể chuyển sang LSP. LSP cho chúng ta biết rằng chúng ta phải có thể sử dụng lớp con bất cứ nơi nào mà lớp cha được mong đợi. Nếu một hàm lấy ArrayListtham số làm và chúng ta vượt qua Nodethay vào đó, không có gì phải thay đổi.
Khả năng tương thích của các lớp con bắt đầu với những thứ đơn giản như chữ ký loại. Khi bạn ghi đè một phương thức, bạn không thể làm cho các kiểu tham số trở nên nghiêm ngặt hơn vì điều đó có thể loại trừ các sử dụng hợp pháp với lớp cha. Nhưng đó là thứ mà trình biên dịch kiểm tra cho chúng ta trong Java.
Nhưng LSP chạy sâu hơn nhiều: Chúng ta phải duy trì khả năng tương thích với mọi thứ được hứa hẹn bằng tài liệu của tất cả các lớp và giao diện cha. Trong câu trả lời của họ , Lynn đã tìm thấy một trường hợp như vậy trong đó Listgiao diện (mà bạn được kế thừa thông qua ArrayList) đảm bảo cách thức equals()và hashCode()các phương thức được cho là hoạt động. Đối với hashCode()bạn thậm chí còn được đưa ra một thuật toán cụ thể phải được thực hiện chính xác. Giả sử bạn đã viết điều này Node:
public class Node extends ArrayList<Node> {
  public final int value;
  public Node(int value, Node... children) {
    this.value = Value;
    for (Node child : children)
      add(child);
  }
  ...
}
Điều này đòi hỏi rằng valuekhông thể đóng góp vào hashCode()và không thể ảnh hưởng equals(). Các Listgiao diện - mà bạn hứa danh dự bằng cách kế thừa từ nó - đòi hỏi new Node(0).equals(new Node(123))đến mức khó tin.
Bởi vì kế thừa từ các lớp làm cho quá dễ dàng để vô tình phá vỡ một lời hứa mà lớp cha mẹ đã thực hiện và vì nó thường phơi bày nhiều phương thức hơn bạn dự định, nên thường đề nghị bạn thích sáng tác hơn kế thừa . Nếu bạn phải kế thừa một cái gì đó, nó được đề xuất chỉ kế thừa giao diện. Nếu bạn muốn sử dụng lại hành vi của một lớp cụ thể, bạn có thể giữ nó như một đối tượng riêng biệt trong một biến thể hiện, theo cách đó, tất cả các lời hứa và yêu cầu của nó không bắt buộc đối với bạn.
Đôi khi, ngôn ngữ tự nhiên của chúng tôi cho thấy mối quan hệ thừa kế: Xe hơi là phương tiện. Một chiếc xe máy là một phương tiện. Tôi có nên định nghĩa các lớp Carvà Motorcyclekế thừa từ một Vehiclelớp không? Thiết kế hướng đối tượng không phải là phản ánh chính xác thế giới thực trong mã của chúng tôi. Chúng ta không thể dễ dàng mã hóa các nguyên tắc phân loại phong phú của thế giới thực trong mã nguồn của mình.
Một ví dụ như vậy là vấn đề mô hình hóa nhân viên-sếp. Chúng tôi có nhiều Persons, mỗi cái có một tên và địa chỉ. An Employeelà một Personvà có một Boss. A Bosscũng là a Person. Vậy tôi có nên tạo một Personlớp được kế thừa bởi Bossvà Employeekhông? Bây giờ tôi có một vấn đề: ông chủ cũng là một nhân viên và có một cấp trên khác. Vì vậy, nó có vẻ như Bossnên mở rộng Employee. Nhưng đó CompanyOwnerlà một Bossnhưng không phải là một Employee? Bất kỳ loại biểu đồ thừa kế bằng cách nào đó sẽ phá vỡ ở đây.
OOP không phải là về phân cấp, kế thừa và sử dụng lại các lớp hiện có, nó là về khái quát hóa hành vi . OOP nói về vấn đề của tôi. Tôi có rất nhiều đối tượng và muốn họ làm một việc cụ thể - và tôi không quan tâm làm thế nào. Đây là giao diện dành cho. Nếu bạn triển khai Iterablegiao diện cho bạn Nodevì bạn muốn làm cho nó có thể lặp lại, điều đó hoàn toàn tốt. Nếu bạn triển khai Collectiongiao diện vì bạn muốn thêm / xóa các nút con, v.v. thì không sao. Nhưng kế thừa từ một lớp khác bởi vì nó tình cờ mang đến cho bạn tất cả những gì không, hoặc ít nhất là không trừ khi bạn đã suy nghĩ cẩn thận như đã nêu ở trên.