2010-02-10 11 views
23

Thành phần và kế thừa.Có thành phần nào không thể thực hiện được thừa kế đó không?

Tôi biết rằng cả hai công cụ đều được chọn khi thích hợp và bối cảnh rất quan trọng trong việc lựa chọn giữa bố cục và kế thừa. Tuy nhiên, các cuộc thảo luận về bối cảnh thích hợp cho mỗi thường là một chút mờ; điều này khiến tôi bắt đầu xem xét cách thức thừa kế và đa hình rõ ràng là những khía cạnh riêng biệt của OOP truyền thống.

Đa hình cho phép người ta chỉ định mối quan hệ "là-a" như nhau cũng như thừa kế. Đặc biệt, kế thừa từ một lớp cơ sở ngầm tạo ra một mối quan hệ đa hình giữa lớp đó và các lớp con của nó. Tuy nhiên, trong khi đa hình có thể được thực hiện bằng cách sử dụng giao diện thuần túy, thừa kế làm phức tạp mối quan hệ đa hình bằng cách đồng thời chuyển các chi tiết thực hiện. Bằng cách này, thừa kế khá khác biệt với tính đa hình thuần túy.

Là một công cụ, thừa kế phục vụ các lập trình viên khác biệt so với đa hình (thông qua giao diện tinh khiết) bằng cách đơn giản hóa việc thực hiện tái sử dụng trong trường hợp tầm thường. Tuy nhiên, trong phần lớn các trường hợp, các chi tiết thực hiện của một siêu lớp phụ xung đột một cách tinh tế với các yêu cầu của một lớp con. Đây là lý do tại sao chúng tôi có "ghi đè" và "ẩn thành viên". Trong những trường hợp này, việc tái sử dụng được cung cấp bởi thừa kế được mua với nỗ lực bổ sung của việc xác minh các thay đổi trạng thái và các đường dẫn thực hiện qua các cấp mã: các chi tiết thực hiện "phẳng" hoàn chỉnh của lớp con được trải ra giữa nhiều lớp, thường có nghĩa là nhiều tệp, trong đó chỉ có các phần áp dụng cho lớp con đang được đề cập đến. Nhìn qua hệ thống phân cấp đó là hoàn toàn cần thiết khi giao dịch với thừa kế, bởi vì không nhìn vào mã của siêu lớp, không có cách nào để biết những chi tiết không được ghi đè nào đang làm khỉ với trạng thái của bạn hoặc chuyển hướng thực hiện của bạn.

Để so sánh, việc sử dụng độc quyền bố cục đảm bảo bạn sẽ thấy trạng thái nào có thể được sửa đổi bởi các đối tượng được hiển thị rõ ràng có phương thức được gọi theo ý của bạn. Việc triển khai thực hiện phẳng vẫn chưa đạt được (và thậm chí không thực sự mong muốn, vì lợi ích của lập trình có cấu trúc là đóng gói và trừu tượng hóa các chi tiết triển khai) nhưng bạn vẫn nhận được mã tái sử dụng, và bạn sẽ chỉ phải tìm ở một nơi khi mã lỗi.

Với mục tiêu thử nghiệm những ý tưởng này trong thực tế, tránh thừa kế truyền thống cho một sự kết hợp của tính đa hình giao diện dựa trên tinh khiết và thành phần đối tượng, tôi tự hỏi,

Có thành phần đối tượng bất cứ điều gì và giao diện không thể thực hiện điều đó thừa kế có thể ?

Sửa

Trong các câu trả lời cho đến nay, ewernli tin không có những kỳ công kỹ thuật sẵn sàng cho một kỹ thuật nhưng không phải là khác; sau đó, ông đề cập đến cách thức các mô hình và phương pháp tiếp cận thiết kế khác nhau vốn có đối với từng kỹ thuật. Điều này là lý do. Tuy nhiên, gợi ý này khiến tôi phải tinh chỉnh câu hỏi của mình bằng cách hỏi liệu việc sử dụng độc quyền các thành phần và giao diện thay cho thừa kế truyền thống có ngăn cấm việc sử dụng bất kỳ mẫu thiết kế chính nào không? Và nếu vậy, không có các mẫu tương đương để sử dụng trong tình huống của tôi?

+2

Cá nhân tôi thích mixin. :) –

+1

Tôi không có thời gian để kiểm tra sao chép hiệu quả, nhưng chủ đề thành phần v. Kế thừa này được truy cập thường xuyên trên SO, cho ví dụ. http://stackoverflow.com/questions/216523/object-oriented-best-practices-inheritance-v-composition-v-interfaces hoặc http://stackoverflow.com/questions/1598722/should-i-use-inheritance -hoặc thành phần. “Người ta luôn có thể xoay quanh chủ đề này và gọi nó là một cuốn tiểu thuyết nắm lấy mọi thứ…” Tuy nhiên, một điều chúng ta nên đồng ý là loại câu hỏi này không dẫn đến những câu trả lời dứt khoát hoặc thậm chí có thẩm quyền. Có lẽ một CW có thể là định dạng phù hợp hơn ... – mjv

+0

Tôi không có nghĩa là để rehash một cuộc tranh luận mệt mỏi. Phải thừa nhận rằng, cách tôi tuyên bố trường hợp của tôi là khá nhiều mẫu tranh luận một chiều, nhưng quan tâm chính của tôi là trả lời có hay không có sử dụng cho thừa kế mà không thể thay thế thành phần và giao diện. p.s., định dạng CW là gì? Có lẽ tôi sẽ thử rằng ... –

Trả lời

21

Về mặt kỹ thuật, mọi thứ có thể được thực hiện với thừa kế cũng có thể được thực hiện với ủy quyền. Vì vậy, câu trả lời sẽ là "không".

Chuyển đổi thừa kế vào đoàn

Hãy nói rằng chúng tôi có các lớp sau đây thực hiện với thừa kế:

public class A { 
    String a = "A"; 
    void doSomething() { .... } 
    void getDisplayName() { return a } 
    void printName { System.out.println(this.getDisplayName() }; 
} 

public class B extends A { 
    String b = "B"; 
    void getDisplayName() { return a + " " + b; } 
    void doSomething() { super.doSomething() ; ... }  
} 

Các công cụ hoạt động độc đáo, và kêu gọi printName trên một thể hiện của B sẽ in "A B" trong giao diện điều khiển.

Bây giờ, nếu chúng ta viết lại rằng với phái đoàn, chúng tôi nhận được:

public class A { 
    String a = "A"; 
    void doSomething() { .... } 
    void getDisplayName() { return a } 
    void printName { System.out.println(this.getName() }; 
} 

public class B { 
    String b = "B"; 
    A delegate = new A(); 
    void getDisplayName() { return delegate.a + " " + b; } 
    void doSomething() { delegate.doSomething() ; ... } 
    void printName() { delegate.printName() ; ... } 
} 

Chúng ta cần phải xác định printName trong B và cũng để tạo ra các đại biểu khi B được khởi tạo. Một cuộc gọi đến doSomething sẽ hoạt động theo cách tương tự như với kế thừa. Nhưng một cuộc gọi đến printName sẽ in "A" trong bảng điều khiển. Thật vậy với đoàn đại biểu, chúng tôi đã mất khái niệm mạnh mẽ về "điều này" đang bị ràng buộc đối tượng dụ và các phương thức cơ bản có thể gọi các phương thức đã được ghi đè.

Điều này có thể được giải quyết bằng ngôn ngữ hỗ trợ phái đoàn thuần túy. Với đoàn đại biểu thuần túy, "điều này" trong đại biểu sẽ vẫn tham chiếu trường hợp B. Điều đó có nghĩa là this.getName() sẽ bắt đầu gửi phương thức từ lớp B. Chúng tôi đạt được điều tương tự như với thừa kế. Đây là cơ chế được sử dụng trong ngôn ngữ prototype-based chẳng hạn như Self có ủy quyền có tính năng tích hợp sẵn (Bạn có thể đọc here cách thức hoạt động của kế thừa trong Tự).

Nhưng Java không có ủy quyền thuần túy. Khi nào thì bị kẹt? Không thực sự, chúng tôi vẫn có thể làm được điều đó chúng ta có một số nỗ lực nhiều hơn:

public class A implements AInterface { 
    String a = "A"; 
    AInterface owner; // replace "this" 
    A (AInterface o) { owner = o } 
    void doSomething() { .... } 
    void getDisplayName() { return a } 
    void printName { System.out.println(owner.getName() }; 
} 

public class B implements AInterface { 
    String b = "B"; 
    A delegate = new A(this); 
    void getDisplayName() { return delegate.a + " " + b; } 
    void doSomething() { delegate.doSomething() ; ... } 
    void printName() { delegate.printName() ; ... } 
} 

Chúng tôi về cơ bản lại thực hiện những gì được xây dựng trong thừa kế cung cấp. Nó có ý nghĩa không? Không thực sự. Nhưng nó minh họa rằng thừa kế luôn có thể được chuyển đổi thành phái đoàn.

Thảo luận

thừa kế được đặc trưng bởi thực tế là một lớp cơ sở có thể gọi một phương thức được ghi đè trong một lớp học phụ. Đây là ví dụ bản chất của template pattern. Những điều như vậy không thể được thực hiện dễ dàng với phái đoàn. Mặt khác, đây chính là điều khiến cho thừa kế khó sử dụng. Nó đòi hỏi một bước ngoặt về tinh thần để hiểu được nơi công văn đa hình xảy ra và hiệu quả là gì nếu các phương thức bị ghi đè.

Có một số cạm bẫy đã biết về di sản và sự mong manh mong manh nó có thể giới thiệu trong thiết kế. Đặc biệt nếu hệ thống phân cấp lớp phát triển. Cũng có thể có một số vấn đề với equality trong hashCodeequals nếu kế thừa được sử dụng. Nhưng mặt khác, nó vẫn là một cách rất thanh lịch để giải quyết một số vấn đề.

Ngoài ra, ngay cả khi thừa kế có thể được thay thế bằng đoàn, một bạn có thể tranh luận rằng họ vẫn đạt được mục đích khác nhau và bổ sung cho nhau - họ không truyền đạt cùng ý mà không bị bắt bởi tinh khiết kỹ thuật tương đương.

(Lý thuyết của tôi là khi ai đó bắt đầu thực hiện OO, chúng tôi bị cám dỗ để sử dụng quá mức thừa kế vì nó cảm nhận như một tính năng của ngôn ngữ.Sau đó, chúng tôi tìm hiểu đoàn đại biểu đó là mô hình/cách tiếp cận và chúng tôi học cách thích nó là tốt. Sau một thời gian, chúng tôi tìm thấy một sự cân bằng giữa cả hai và phát triển ý thức trực giác của cái nào tốt hơn trong trường hợp đó. Vâng, như bạn thấy, tôi vẫn thích cả hai, và cả hai xứng đáng một số thận trọng trước khi được giới thiệu.)

Một số tài liệu

Thừa kế và phái đoàn là các phương thức thay thế cho định nghĩa và chia sẻ gia tăng . Nó có thường được tin rằng phái đoàn cung cấp một mô hình mạnh mẽ hơn. Giấy này chứng minh rằng có một mô hình thừa kế "tự nhiên" " ghi lại tất cả các thuộc tính của số ủy quyền. Độc lập, một số ràng buộc nhất định về khả năng của ủy quyền ghi lại di sản được hiển thị . Cuối cùng, một khung công tác mới hoàn toàn chụp cả hai phái đoàn và thừa kế được phác thảo, và một số của các nhánh của mô hình lai này được khám phá.

Một trong những vấn đề nhất-khái niệm hấp dẫn và hầu hết cùng lúc trong lập trình hướng đối tượng là thừa kế. Thừa kế thường là được coi là tính năng mà phân biệt đối tượng theo định hướng lập trình từ các mô hình lập trình hiện đại khác, nhưng các nhà nghiên cứu hiếm khi đồng ý về ý nghĩa và cách sử dụng của nó. [...]

Bởi vì các khớp nối mạnh mẽ của các tầng lớp và sự gia tăng của các thành viên lớp không cần thiết gây ra bởi thừa kế, những gợi ý để sử dụng thành phần và đoàn đại biểu thay vì có trở nên phổ biến. Việc trình bày một phép tái cấu trúc tương ứng trong tài liệu có thể dẫn người ta tin rằng sự biến đổi như vậy là một việc thực hiện đơn giản. [...]

+0

Là một phần của trích đoạn này từ một bài báo được công bố ? Nếu vậy, tôi sẽ quan tâm đến một liên kết hoặc doi. –

+2

Đoạn đầu tiên là của tôi và là ý kiến ​​của tôi. Sau đó, có 3 tham chiếu đến các giấy tờ liên quan đến chủ đề mà tôi đã trích dẫn tóm tắt. Các doi có thể được tìm thấy trong các liên kết mà tất cả các điểm đến portal.acm.org. Xin lỗi nếu điều đó không rõ ràng. Hãy cho tôi biết nếu bạn không thể tìm thấy chúng dưới dạng pdf. – ewernli

+0

Cảm ơn bạn, tôi đánh giá cao việc làm rõ. –

3

Thành phần không thể vít lên cuộc sống cũng như thừa kế, khi các lập trình viên súng nhanh chóng cố gắng giải quyết vấn đề bằng cách thêm các phương pháp và mở rộng hệ thống phân cấp (chứ không phải đưa ra một ý nghĩ để phân cấp tự nhiên)

Thành phần không thể kết quả là kim cương lạ, khiến các đội bảo dưỡng đốt cháy dầu đêm gãi đầu

Thừa kế là bản chất của thảo luận trong các mẫu thiết kế GOF mà sẽ không giống nhau nếu các lập trình viên sử dụng Thành phần ngay từ đầu.

+0

Đánh dấu tôi là một troll, nhưng tôi đã làm tất cả những sai lầm này trước khi học từ chúng – questzen

1

Xem xét bộ công cụ gui.

Điều khiển chỉnh sửa là cửa sổ, nó sẽ kế thừa các chức năng đóng/bật/sơn của cửa sổ - nó không chứa cửa sổ. Sau đó, một điều khiển văn bản phong phú nên chứa các điều khiển chỉnh sửa lưu/đọc/cắt/dán chức năng nó sẽ rất khó sử dụng nếu nó chỉ chứa một cửa sổ và một điều khiển chỉnh sửa.

+0

Khi thảo luận về các lớp GUI, tôi nghĩ quá dễ nhầm lẫn với bố cục hình ảnh với thành phần lớp học. Người ta có thể tạo IWindow tuyên bố chữ ký cho các đối tượng giống như cửa sổ, và cửa sổ để thực sự vẽ các cửa sổ và xử lý các sự kiện. Sau đó tạo IEditControl và EditControl, thực hiện IWindow. EditControl chỉ đơn giản là ủy nhiệm các trách nhiệm IWindow của nó cho đối tượng Window. Công khai, EditControl trông giống như bất kỳ IWindow nào khác, và hoạt động như Window với các chỉnh sửa EditControl. Cuối cùng, RichTextControl triển khai IEditControl, ủy quyền cho đối tượng EditControl của riêng nó. Mẫu có thể chuỗi. –

+1

Đúng, hầu hết các ví dụ về kế thừa (các lớp nhân viên, vv) được thực hiện tốt hơn dưới dạng bố cục. Nhưng bộ công cụ gui thực sự được hưởng lợi từ thừa kế (IMHO) –

+2

Một cách tôn trọng: Công ty của tôi duy trì một hệ thống kế thừa khổng lồ trong .Net; Theo ví dụ của MSFT với WinForms, các nhà phát triển ban đầu đã sử dụng rất nhiều thừa kế để triển khai các lớp UI. Chúng tôi có 18 hộp kết hợp độc đáo (mặc dù tương tự), 12 biểu mẫu cơ sở và một hệ thống phân cấp thừa kế lên đến 8 cấp độ sâu hơn CLI. Khung giao diện người dùng của chúng tôi là một mê cung, quá mâu thuẫn và mong manh, nhóm phát triển hiện tại đang sợ hãi về sự thay đổi nhỏ nhất đối với nó. Nhưng, thông thường, tất cả các mã này đều thiếu các nhu cầu mới ... Thành phần có nghĩa là chúng ta có thể chọn và chọn các tính năng nếu cần, thay vì kết thúc * phát sinh * các kết hợp mới. –

1

Tôi có thể sai về điều này, nhưng tôi sẽ nói điều đó, và nếu có ai đó có lý do tôi sai, vui lòng chỉ trả lời nhận xét và không bỏ phiếu cho tôi. Có 1 tình huống tôi có thể nghĩ về nơi thừa kế sẽ vượt trội so với sáng tác.

Giả sử tôi có một thư viện Widget nguồn đóng Tôi đang sử dụng trong một dự án (có nghĩa là các chi tiết thực hiện là một bí ẩn đối với tôi bên cạnh những gì được ghi lại). Bây giờ giả sử mỗi widget có khả năng thêm các widget con. Với sự kế thừa, tôi có thể mở rộng lớp Widget để tạo ra một CustomWidget, và sau đó thêm CustomWidget làm tiện ích con của bất kỳ tiện ích con nào khác trong thư viện. Sau đó, mã của tôi để thêm CustomWidget sẽ trông giống như sau:

Widget baseWidget = new Widget(); 
CustomWidget customWidget = new CustomWidget(); 
baseWidget.addChildWidget(customWidget); 

Rất sạch sẽ và tuân thủ các quy ước của thư viện để thêm tiện ích con. Tuy nhiên, với thành phần, nó sẽ phải là cái gì đó như thế này:

Widget baseWidget = new Widget(); 
CustomWidget customWidget = new CustomWidget(); 
customWidget.addAsChildToWidget(baseWidget); 

Không như sạch sẽ, và cũng có thể phá vỡ những quy ước của thư viện

Bây giờ tôi không nói rằng bạn không thể thực hiện điều này với bố cục (trên thực tế ví dụ của tôi cho thấy bạn rất rõ ràng có thể), nó không chỉ lý tưởng trong mọi hoàn cảnh, và có thể dẫn đến phá vỡ các quy ước và các giải pháp thay thế trực quan không hấp dẫn khác.

+2

Bạn nhấn mạnh Widget nằm trong thư viện nguồn mở bên ngoài. Nếu Widget.addChildWidget() lấy một đối số của kiểu Widget, thì kế thừa IMO là lựa chọn duy nhất khả thi. Thư viện có cha mẹ theo dõi trẻ em, nhưng ví dụ sáng tác của bạn yêu cầu CustomWidgets để theo dõi cha mẹ của họ thay vì được theo dõi bởi họ. (Yuck!) Nếu Widget.addChildWidget() lấy một đối số của loại IWidget, bố cục có thể vẫn hoạt động, vì CustomWidget có thể triển khai IWidget, trong khi trì hoãn một số hành vi cho một Widget riêng tư. btw, Sức mạnh thực sự của Thành phần giao diện đi kèm với IOC & DI. –

-1

Có. Nó là Run Time Type Identification (RTTI).

+0

Nếu bạn cần RTTI, thẳng thắn, bạn đang vi phạm DIP. Nhưng meh. Có một giao diện khai báo một 'getType()' hoặc hàm tương tự, và thực hiện giao diện. Bây giờ bạn có định nghĩa của riêng bạn về "loại", mà bạn có thể đúc thành bất cứ điều gì bạn thích ... và các lớp học liên quan không cần phải có liên quan với nhau. – cHao