Nhiều trường hợp sử dụng thừa kế


15

Java bỏ qua nhiều kế thừa với lý do nó che khuất mục tiêu thiết kế để giữ cho ngôn ngữ đơn giản .

Tôi tự hỏi nếu Java (với hệ sinh thái của nó) thực sự "đơn giản". Python không phức tạp và có nhiều kế thừa. Vì vậy, không quá chủ quan, câu hỏi của tôi là ...

Các mẫu vấn đề điển hình được hưởng lợi từ một mã được thiết kế để sử dụng nhiều kế thừa


8
Java bỏ qua rất nhiều điều hoàn toàn tốt vì rất ít lý do. Tôi sẽ không mong đợi một lời biện minh đàng hoàng cho MI.
DeadMG

2
Nhiều kế thừa của Python chắc chắn lãnh thổ của loài rồng . Việc nó sử dụng độ phân giải tên sâu, từ trái sang phải có các vấn đề quan trọng cho cả khả năng duy trì và hiểu. Mặc dù nó có thể hữu ích trong các hệ thống phân cấp lớp nông, nhưng nó có thể là một phản ứng cực kỳ trực quan trong những thứ sâu sắc.
Đánh dấu gian hàng

Tôi nghĩ lý do mà Java không chứa nhiều sự kế thừa là vì các nhà phát triển Java muốn ngôn ngữ của họ dễ học. Đa kế thừa, trong khi cực kỳ mạnh mẽ trong một số trường hợp, khó nắm bắt, và thậm chí còn khó hơn để được sử dụng để có hiệu quả tốt; đó không phải là điều bạn muốn đối đầu với một sinh viên năm nhất lập trình. Ý tôi là: Làm thế nào để bạn giải thích thừa kế ảo cho ai đó đang vật lộn với khái niệm thừa kế? Và, vì nhiều kế thừa cũng không chính xác tầm thường ở phía người triển khai, nên nhà phát triển Java có khả năng mặc dù bỏ qua nó là một chiến thắng cùng có lợi.
cmaster - phục hồi monica

Java được đánh máy trên danh nghĩa. Python thì không. Điều này làm cho nhiều kế thừa dễ dàng hơn nhiều để thực hiện và hiểu trong Python.
Jules

Câu trả lời:


11

Ưu điểm:

  1. Đôi khi nó cho phép mô hình hóa một vấn đề rõ ràng hơn các cách khác để mô hình hóa nó.
  2. Nếu các parrent khác nhau có mục đích trực giao, nó có thể cho phép một số loại kết hợp

Nhược điểm:

  1. Nếu các bậc cha mẹ khác nhau không có mục đích trực giao, điều đó làm cho loại hình này trở nên khó hiểu.
  2. Không dễ để hiểu cách nó được thực hiện trong một ngôn ngữ (bất kỳ ngôn ngữ nào).

Trong C ++, một ví dụ điển hình về tính kế thừa được sử dụng cho các tính năng trực giao tổng hợp là khi bạn sử dụng CRTP , ví dụ, thiết lập một hệ thống thành phần cho trò chơi.

Tôi đã bắt đầu viết một ví dụ nhưng tôi nghĩ một ví dụ trong thế giới thực đáng xem hơn. Một số mã của Ogre3D sử dụng nhiều kế thừa theo cách tốt đẹp và rất trực quan. Ví dụ, lớp Lưới kế thừa từ cả Tài nguyên và AnimationContainer. Các tài nguyên hiển thị giao diện chung cho tất cả các tài nguyên và AnimationContainer hiển thị giao diện cụ thể để thao tác một tập hợp các hình ảnh động. Chúng không liên quan với nhau, vì vậy thật dễ dàng để nghĩ về Lưới là một tài nguyên mà ngoài ra có thể có được một bộ hoạt hình. Cảm thấy tự nhiên phải không?

Bạn có thể nhìn vào các ví dụ khác trong thư viện này , như cách phân bổ bộ nhớ được quản lý theo cách bị phạt bằng cách làm cho các lớp kế thừa từ các biến thể của lớp CRTP nạp chồng mới và xóa.

Như đã nói, các vấn đề chính với nhiều kế thừa tăng lên từ việc trộn các khái niệm liên quan. Nó khiến ngôn ngữ phải thiết lập các triển khai phức tạp (xem cách C ++ cho phép giải quyết vấn đề kim cương ...) và người dùng không chắc chắn những gì đang xảy ra trong quá trình triển khai đó. Ví dụ, đọc bài viết này giải thích cách nó được thực hiện trong C ++ .

Loại bỏ nó khỏi ngôn ngữ giúp tránh những người không biết cách ngôn ngữ bị xâm nhập để làm cho mọi thứ trở nên tồi tệ. Nhưng nó buộc phải suy nghĩ theo cách mà đôi khi, không cảm thấy tự nhiên, ngay cả khi đó là trường hợp cạnh, nó xảy ra thường xuyên hơn mà bạn có thể nghĩ.


Tôi thực sự đánh giá cao nếu bạn tô điểm cho câu trả lời của mình bằng một vấn đề mẫu - điều đó sẽ làm cho các thuật ngữ như "mục đích trực giao" rõ ràng hơn - nhưng cảm ơn
treecoder

Ok hãy để tôi thử thêm một cái gì đó.
Klaim

Ogre3D không phải là nơi tôi muốn tìm cảm hứng thiết kế - bạn đã thấy nhiễm trùng Singleton của họ chưa?
DeadMG

Đầu tiên, người thừa kế singleton không thực sự là người độc thân, việc xây dựng và phá hủy là rõ ràng. Tiếp theo, Ogre là một lớp trên một hệ thống phần cứng (hoặc trình điều khiển đồ họa nếu bạn thích). Điều đó có nghĩa là chỉ nên có một đại diện duy nhất cho các giao diện hệ thống (như Root hoặc các giao diện khác). Họ có thể loại bỏ một số singleton nhưng đó không phải là vấn đề ở đây. Tôi đã tự nguyện tránh để chỉ ra điều này để tránh có một cuộc thảo luận troll, vì vậy xin vui lòng, hãy xem các ví dụ tôi đã chỉ. Việc sử dụng Singleton của họ có thể không hoàn hảo nhưng thực tế nó rất hữu ích trong thực tế (nhưng chỉ đối với loại hệ thống của họ không phải là tất cả).
Klaim

4

Có một khái niệm gọi là mixins được sử dụng nhiều trong các ngôn ngữ năng động hơn. Đa kế thừa là một cách trong đó mixin có thể được hỗ trợ bởi một ngôn ngữ. Mixins thường được sử dụng như một cách để một lớp tích lũy các phần chức năng khác nhau. Nếu không có nhiều kế thừa, bạn phải sử dụng phép gộp / ủy nhiệm để có được hành vi loại mixin với một lớp, đó là một cú pháp nặng hơn một chút.


+1 đây thực sự là một lý do tốt để có nhiều kế thừa. Mixins mang ý nghĩa bổ sung ("lớp này không nên được sử dụng dưới dạng độc lập")
tro999

2

Tôi nghĩ rằng sự lựa chọn chủ yếu dựa trên các vấn đề do vấn đề kim cương .

Hơn nữa, thường có thể tránh được việc sử dụng nhiều quyền thừa kế bằng cách ủy quyền hoặc các phương tiện khác.

Tôi không chắc về ý nghĩa của câu hỏi cuối cùng của bạn. Nhưng nếu "trong trường hợp nào thì đa thừa kế có hữu ích không?", Thì trong tất cả các trường hợp bạn muốn có một đối tượng A có chức năng của các đối tượng B và C, về cơ bản.


2

Tôi sẽ không nghiên cứu nhiều ở đây nhưng bạn chắc chắn có thể hiểu được nhiều sự kế thừa trong python thông qua liên kết sau http://docs.python.org/release/1.5.1p1/tut/mult Môn.html :

Quy tắc duy nhất cần thiết để giải thích ngữ nghĩa là quy tắc phân giải được sử dụng cho các tham chiếu thuộc tính lớp. Đây là chiều sâu đầu tiên, từ trái sang phải. Do đó, nếu một thuộc tính không được tìm thấy trong DeruredClassName, thì nó được tìm kiếm trong Base1, sau đó (đệ quy) trong các lớp cơ sở của Base1, và chỉ khi nó không được tìm thấy ở đó, thì nó mới được tìm kiếm trong Base2, v.v.

...

Rõ ràng rằng việc sử dụng nhiều quyền thừa kế bừa bãi là một cơn ác mộng bảo trì, do sự phụ thuộc vào Python vào các quy ước để tránh xung đột tên vô tình. Một vấn đề nổi tiếng với nhiều kế thừa là một lớp xuất phát từ hai lớp tình cờ có một lớp cơ sở chung. Mặc dù đủ dễ để tìm hiểu điều gì xảy ra trong trường hợp này (cá thể sẽ có một bản sao duy nhất của `` biến đối tượng '' hoặc các thuộc tính dữ liệu được sử dụng bởi lớp cơ sở chung), không rõ là các ngữ nghĩa này theo bất kỳ cách nào hữu ích.

Đây chỉ là một đoạn nhỏ nhưng đủ lớn để xóa tan những nghi ngờ tôi đoán.


1

Một nơi mà nhiều kế thừa sẽ hữu ích là một tình huống trong đó một lớp thực hiện một số giao diện, nhưng bạn muốn có một số chức năng mặc định được tích hợp cho mỗi giao diện. Điều này rất hữu ích nếu hầu hết các lớp thực hiện một số giao diện muốn làm một cái gì đó theo cùng một cách, nhưng đôi khi bạn cần phải làm một cái gì đó khác nhau. Bạn có thể có mỗi lớp với cùng một triển khai, nhưng sẽ có ý nghĩa hơn khi đẩy nó lên một vị trí.


1
Điều đó sẽ yêu cầu nhiều kế thừa tổng quát, hay đơn giản là một phương tiện mà giao diện có thể chỉ định các hành vi mặc định cho các phương thức chưa được thực hiện? Nếu các giao diện chỉ có thể chỉ định triển khai mặc định cho các phương thức mà chúng tự thực hiện (trái ngược với các phương thức chúng thừa hưởng từ các giao diện khác) thì tính năng đó sẽ hoàn toàn tránh được các vấn đề kim cương kép gây khó khăn cho việc thừa kế nhiều lần.
supercat

1

Các mẫu vấn đề điển hình được hưởng lợi từ một mã được thiết kế để sử dụng nhiều kế thừa là gì?

Đây chỉ là một ví dụ nhưng tôi thấy vô giá để cải thiện sự an toàn và giảm thiểu những cám dỗ để áp dụng thay đổi xếp tầng trong cả người gọi hoặc lớp con.

Nơi tôi đã tìm thấy nhiều sự kế thừa vô cùng hữu ích ngay cả đối với các giao diện không trạng thái trừu tượng nhất là thành ngữ giao diện không ảo (NVI) trong C ++.

Chúng thậm chí không thực sự là các lớp cơ sở trừu tượng nhiều như giao diện chỉ cần thực hiện một chút để thực thi các khía cạnh phổ biến của hợp đồng, vì chúng không thực sự thu hẹp tính tổng quát của hợp đồng nhiều hơn là thực thi tốt hơn .

Ví dụ đơn giản (một số có thể kiểm tra xem một tệp xử lý được truyền vào đang mở hay đại loại như thế):

// Non-virtual interface (public methods are nonvirtual/final).
// Since these are modeling the concept of "interface", not ABC,
// multiple will often be inherited ("implemented") by a subclass.
class SomeInterface
{
public:
    // Pre: x should always be greater than or equal to zero.
    void f(int x) /*final*/
    {
        // Make sure x is actually greater than or equal to zero
        // to meet the necessary pre-conditions of this function.
        assert(x >= 0);

        // Call the overridden function in the subtype.
        f_impl(x);
    }

protected:
    // Overridden by a boatload of subtypes which implement
    // this non-virtual interface.
    virtual void f_impl(int x) = 0;
};

Trong trường hợp này, có thể fđược gọi bởi một ngàn vị trí trong cơ sở mã, trong khif_impl bị ghi đè bởi một trăm lớp con.

Sẽ rất khó để thực hiện loại kiểm tra an toàn này ở tất cả 1000 địa điểm gọi fhoặc tất cả 100 địa điểm ghi đèf_impl .

Bằng cách làm cho mục nhập này trở thành chức năng không ảo, nó cho tôi một vị trí trung tâm để thực hiện kiểm tra này. Và kiểm tra này không làm giảm sự trừu tượng trong một chút, vì nó chỉ đơn giản là khẳng định một điều kiện tiên quyết cần thiết để gọi hàm này. Theo một nghĩa nào đó, có thể củng cố hợp đồng được cung cấp bởi giao diện và giảm bớt gánh nặng kiểm tra xđầu vào để đảm bảo nó tuân thủ các điều kiện tiên quyết hợp lệ ở tất cả 100 địa điểm ghi đè lên nó.

Đó là điều tôi ước mọi ngôn ngữ đều có, và cũng mong muốn, ngay cả trong C ++, đó là một khái niệm bản địa hơn một chút (ví dụ: không yêu cầu chúng tôi xác định một chức năng riêng để ghi đè).

Điều này cực kỳ hữu ích nếu bạn không làm điều này asserttrước và nhận ra rằng bạn cần nó sau này khi một số vị trí ngẫu nhiên trong cơ sở mã đang gặp phải các giá trị âm được chuyển đến f.


0

Thứ nhất: nhiều bản sao của lớp cơ sở (một vấn đề C ++) & khớp nối chặt chẽ giữa các lớp cơ sở và các lớp dẫn xuất.

Thứ hai: nhiều kế thừa từ các giao diện trừu tượng


bạn đang đề nghị nó không hữu ích trong bất kỳ bối cảnh nào? Và rằng mọi thứ có thể được thiết kế / mã hóa thuận tiện mà không cần nó? Ngoài ra xin vui lòng giải thích về điểm thứ hai.
treecoder
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.