Thích thành phần hơn thừa kế?

1670
Readonly 2008-09-08 15:58.

Tại sao lại thích sáng tác hơn là thừa kế? Có những đánh đổi nào cho mỗi cách tiếp cận? Khi nào bạn nên chọn kế thừa thay vì thành phần?

30 answers

1237
Gishu 2008-09-10 17:04.

Ưu tiên bố cục hơn là kế thừa vì nó dễ uốn / dễ sửa đổi hơn sau này, nhưng không sử dụng cách tiếp cận luôn soạn. Với bố cục, thật dễ dàng để thay đổi hành vi một cách nhanh chóng với Dependency Injection / Setters. Tính kế thừa là khó khăn hơn vì hầu hết các ngôn ngữ không cho phép bạn bắt nguồn từ nhiều hơn một loại. Vì vậy, ngỗng ít nhiều đã được nấu chín một khi bạn bắt nguồn từ TypeA.

Thử nghiệm axit của tôi cho những điều trên là:

  • TypeB có muốn hiển thị giao diện hoàn chỉnh (tất cả các phương thức công khai không hơn không kém) của TypeA để TypeB có thể được sử dụng khi TypeA được mong đợi không? Chỉ ra sự kế thừa .

    • ví dụ: Một máy bay hai cánh Cessna sẽ hiển thị giao diện hoàn chỉnh của một máy bay, nếu không nhiều hơn. Vì vậy, điều đó làm cho nó phù hợp để bắt nguồn từ Máy bay.
  • Có phải TypeB chỉ muốn một số / một phần của hành vi được hiển thị bởi TypeA không? Cho biết nhu cầu về Bố cục.

    • ví dụ như Chim có thể chỉ cần hành vi bay của Máy bay. Trong trường hợp này, nên giải nén nó ra dưới dạng giao diện / lớp / cả hai và biến nó thành thành viên của cả hai lớp.

Cập nhật: Vừa quay lại câu trả lời của tôi và có vẻ như bây giờ nó chưa hoàn chỉnh nếu không đề cập cụ thể đến Nguyên tắc thay thế Liskov của Barbara Liskov như một bài kiểm tra cho 'Tôi có nên kế thừa từ loại này không?'

434
Nick Zalutskiy 2008-09-08 16:09.

Hãy nghĩ về sự ngăn chặn như một mối quan hệ. Một chiếc xe "có động cơ", một người "có" tên, v.v.

Hãy coi thừa kế là một mối quan hệ. Xe hơi "là" phương tiện, người "là" động vật có vú, v.v.

Tôi không có tín dụng cho cách tiếp cận này. Tôi lấy nó thẳng từ Ấn bản thứ hai của Bộ mã hoàn chỉnh của Steve McConnell , Phần 6.3 .

219
aleemb 2009-05-21 21:54.

Nếu bạn hiểu sự khác biệt, bạn sẽ dễ dàng giải thích hơn.

Mã thủ tục

Một ví dụ về điều này là PHP không sử dụng các lớp (đặc biệt là trước PHP5). Tất cả logic được mã hóa trong một tập hợp các chức năng. Bạn có thể bao gồm các tệp khác chứa các hàm trợ giúp, v.v. và tiến hành logic nghiệp vụ của mình bằng cách chuyển dữ liệu xung quanh trong các hàm. Điều này có thể rất khó quản lý khi ứng dụng phát triển. PHP5 cố gắng khắc phục điều này bằng cách cung cấp thiết kế hướng đối tượng hơn.

Di sản

Điều này khuyến khích việc sử dụng các lớp. Kế thừa là một trong ba nguyên lý của thiết kế OO (kế thừa, đa hình, đóng gói).

class Person {
   String Title;
   String Name;
   Int Age
}

class Employee : Person {
   Int Salary;
   String Title;
}

Đây là sự kế thừa trong công việc. Nhân viên "là một" Người hoặc thừa kế từ Người. Tất cả các mối quan hệ thừa kế đều là mối quan hệ "is-a". Nhân viên cũng phủ bóng thuộc tính Tiêu đề từ Người, có nghĩa là Nhân viên.Title sẽ trả lại Tiêu đề cho Nhân viên không phải Người.

Thành phần

Thành phần được ưu tiên hơn là thừa kế. Nói một cách rất đơn giản, bạn sẽ có:

class Person {
   String Title;
   String Name;
   Int Age;

   public Person(String title, String name, String age) {
      this.Title = title;
      this.Name = name;
      this.Age = age;
   }

}

class Employee {
   Int Salary;
   private Person person;

   public Employee(Person p, Int salary) {
       this.person = p;
       this.Salary = salary;
   }
}

Person johnny = new Person ("Mr.", "John", 25);
Employee john = new Employee (johnny, 50000);

Thành phần thường có mối quan hệ "có một" hoặc "sử dụng một". Ở đây lớp Nhân viên có một Người. Nó không kế thừa từ Person mà thay vào đó, đối tượng Person được chuyển cho nó, đó là lý do tại sao nó "có" Person.

Thành phần thừa kế

Bây giờ, giả sử bạn muốn tạo một loại Trình quản lý để kết thúc với:

class Manager : Person, Employee {
   ...
}

Ví dụ này sẽ hoạt động tốt, tuy nhiên, điều gì sẽ xảy ra nếu Người và Nhân viên đều khai báo Title? Manager.Title nên trả lại "Người quản lý hoạt động" hay "Ông"? Theo bố cục, sự không rõ ràng này được xử lý tốt hơn:

Class Manager {
   public string Title;
   public Manager(Person p, Employee e)
   {
      this.Title = e.Title;
   }
}

Đối tượng Manager bao gồm một Nhân viên và một Người. Hành vi Tiêu đề được lấy từ nhân viên. Thành phần rõ ràng này loại bỏ sự mơ hồ giữa những thứ khác và bạn sẽ gặp ít lỗi hơn.

156
Galilyou 2009-05-21 22:34.

Với tất cả những lợi ích không thể phủ nhận do kế thừa mang lại, đây là một số nhược điểm của nó.

Nhược điểm của Thừa kế:

  1. Bạn không thể thay đổi việc triển khai được kế thừa từ các siêu lớp trong thời gian chạy (rõ ràng là vì kế thừa được xác định tại thời điểm biên dịch).
  2. Kế thừa cho thấy một lớp con chi tiết về việc triển khai lớp cha của nó, đó là lý do tại sao người ta thường nói rằng kế thừa phá vỡ tính đóng gói (theo nghĩa là bạn thực sự cần tập trung vào các giao diện chứ không phải việc triển khai, vì vậy việc sử dụng lại bằng cách phân lớp không phải lúc nào cũng được ưu tiên).
  3. Sự kết hợp chặt chẽ được cung cấp bởi kế thừa làm cho việc triển khai một lớp con bị ràng buộc rất nhiều với việc triển khai một lớp siêu mà bất kỳ thay đổi nào trong việc triển khai cha sẽ buộc lớp con thay đổi.
  4. Việc tái sử dụng quá nhiều bằng cách phân loại con có thể làm cho ngăn xếp kế thừa rất sâu và rất khó hiểu.

Mặt khác, thành phần đối tượng được xác định trong thời gian chạy thông qua các đối tượng có được tham chiếu đến các đối tượng khác. Trong trường hợp này, các đối tượng này sẽ không bao giờ có thể tiếp cận dữ liệu được bảo vệ của nhau (không có sự phá vỡ đóng gói) và sẽ buộc phải tôn trọng giao diện của nhau. Và trong trường hợp này, các phụ thuộc thực thi sẽ ít hơn rất nhiều so với trường hợp kế thừa.

89
Tim Howland 2008-09-08 16:48.

Một lý do khác, rất thực dụng, để thích thành phần hơn là kế thừa có liên quan đến mô hình miền của bạn và ánh xạ nó tới cơ sở dữ liệu quan hệ. Thực sự rất khó để ánh xạ kế thừa sang mô hình SQL (bạn sẽ gặp phải tất cả các cách giải quyết khó hiểu, như tạo các cột không phải lúc nào cũng được sử dụng, sử dụng các khung nhìn, v.v.) Một số ORML cố gắng giải quyết vấn đề này, nhưng nó luôn trở nên phức tạp một cách nhanh chóng. Thành phần có thể dễ dàng được mô hình hóa thông qua mối quan hệ khóa ngoại giữa hai bảng, nhưng kế thừa thì khó hơn nhiều.

87
Pavel Feldman 2009-05-09 12:29.

Trong khi nói ngắn gọn, tôi đồng ý với "Thích sáng tác hơn là thừa kế", nhưng đối với tôi, nó thường giống như "thích khoai tây hơn coca-cola". Có nơi để kế thừa và nơi để sáng tác. Bạn cần phải hiểu sự khác biệt, sau đó câu hỏi này sẽ biến mất. Điều thực sự có ý nghĩa đối với tôi là "nếu bạn định sử dụng quyền thừa kế - hãy nghĩ lại, rất có thể bạn cần bố cục".

Bạn nên thích khoai tây hơn coca cola khi bạn muốn ăn, và coca cola hơn khoai tây khi bạn muốn uống.

Tạo một lớp con không chỉ là một cách thuận tiện để gọi các phương thức của lớp cha. Bạn nên sử dụng kế thừa khi lớp con "là-một" siêu lớp cả về mặt cấu trúc và chức năng, khi nó có thể được sử dụng làm lớp cha và bạn sẽ sử dụng nó. Nếu nó không phải là trường hợp - nó không phải là kế thừa, mà là một cái gì đó khác. Thành phần là khi các đối tượng của bạn bao gồm một đối tượng khác hoặc có mối quan hệ nào đó với chúng.

Vì vậy, đối với tôi, có vẻ như nếu ai đó không biết liệu anh ta có cần kế thừa hay thành phần hay không, vấn đề thực sự là anh ta không biết mình muốn uống hay ăn. Suy nghĩ về miền vấn đề của bạn nhiều hơn, hiểu rõ hơn về miền đó.

62
Mike Valenty 2010-01-24 09:55.

Quyền thừa kế khá hấp dẫn, đặc biệt là đến từ đất đai thủ tục và nó thường trông thanh lịch một cách lừa dối. Ý tôi là tất cả những gì tôi cần làm là thêm một chút chức năng này vào một số lớp khác, phải không? Chà, một trong những vấn đề là

kế thừa có lẽ là hình thức kết hợp tồi tệ nhất mà bạn có thể có

Lớp cơ sở của bạn phá vỡ tính đóng gói bằng cách tiết lộ chi tiết triển khai cho các lớp con dưới dạng các thành viên được bảo vệ. Điều này làm cho hệ thống của bạn trở nên cứng nhắc và dễ hỏng. Tuy nhiên, lỗ hổng bi thảm hơn là lớp con mới mang theo tất cả hành trang và quan điểm của chuỗi kế thừa.

Bài viết, Inheritance is Evil: The Epic Failure of the DataAnnotationsModelBinder , giới thiệu một ví dụ về điều này trong C #. Nó cho thấy việc sử dụng tính kế thừa khi thành phần lẽ ra nên được sử dụng và cách nó có thể được cấu trúc lại.

45
lcn 2015-09-14 19:22.

Không tìm thấy câu trả lời thỏa đáng ở đây, vì vậy tôi đã viết một cái mới.

Để hiểu tại sao " thích thành phần hơn thừa kế", trước tiên chúng ta cần lấy lại giả định đã bỏ qua trong thành ngữ rút gọn này.

Có hai lợi ích của việc kế thừa: phân loại con và phân lớp con

  1. Kiểu phụ có nghĩa là tuân theo một kiểu (giao diện) chữ ký, tức là một tập hợp các API và người ta có thể ghi đè một phần của chữ ký để đạt được tính đa hình của kiểu phụ.

  2. Phân lớp có nghĩa là sử dụng lại ngầm định các triển khai phương thức.

Với hai lợi ích, hai mục đích khác nhau để thực hiện kế thừa: định hướng nhập kiểu con và định hướng tái sử dụng mã.

Nếu việc sử dụng lại mã là mục đích duy nhất , thì phân lớp có thể cung cấp nhiều hơn những gì anh ta cần, tức là một số phương thức công khai của lớp cha không có nhiều ý nghĩa đối với lớp con. Trong trường hợp này, thay vì ưu tiên bố cục hơn là thừa kế, thì bố cục được yêu cầu . Đây cũng là nơi xuất phát khái niệm "is-a" so với "has-a".

Vì vậy, chỉ khi định kiểu con, tức là để sử dụng lớp mới sau này theo cách đa hình, chúng ta mới phải đối mặt với vấn đề chọn kế thừa hoặc thành phần. Đây là giả định được bỏ qua trong thành ngữ rút gọn đang thảo luận.

Đối với kiểu phụ là tuân theo một chữ ký kiểu, điều này có nghĩa là thành phần luôn phải hiển thị không ít API của kiểu. Bây giờ sự đánh đổi bắt đầu:

  1. Tính năng thừa kế cung cấp khả năng tái sử dụng mã dễ dàng nếu không bị ghi đè, trong khi thành phần phải mã lại mọi API, ngay cả khi đó chỉ là một công việc ủy ​​quyền đơn giản.

  2. Kế thừa cung cấp đệ quy mở đơn giản thông qua trang web đa hình bên trong this, tức là gọi phương thức ghi đè (hoặc thậm chí kiểu ) trong một hàm thành viên khác, công khai hoặc riêng tư (mặc dù không được khuyến khích ). Đệ quy mở có thể được mô phỏng thông qua thành phần , nhưng nó đòi hỏi nhiều nỗ lực hơn và có thể không phải lúc nào cũng khả thi (?). Đây câu trả lời cho một câu hỏi trùng lặp nói một cái gì đó tương tự.

  3. Kế thừa cho thấy các thành viên được bảo vệ . Điều này phá vỡ tính đóng gói của lớp cha và nếu được lớp con sử dụng, một phụ thuộc khác giữa con và cha của nó sẽ được đưa vào.

  4. Thành phần có lợi ích của sự đảo ngược kiểm soát và sự phụ thuộc của nó có thể được đưa vào động, như được hiển thị trong mẫu trang trí và mẫu proxy .

  5. Thành phần có lợi ích của lập trình hướng tổ hợp , tức là hoạt động theo cách giống như mẫu kết hợp .

  6. Thành phần ngay sau khi lập trình cho một giao diện .

  7. Thành phần có lợi ích là dễ dàng thừa kế .

Với những đánh đổi ở trên, chúng tôi thích thành phần hơn là thừa kế. Tuy nhiên, đối với các lớp có liên quan chặt chẽ, tức là khi việc sử dụng lại mã ngầm thực sự mang lại lợi ích, hoặc sức mạnh ma thuật của đệ quy mở được mong muốn, kế thừa sẽ là lựa chọn.

43
dance2die 2008-09-08 16:25.

Trong Java hoặc C #, một đối tượng không thể thay đổi kiểu của nó khi nó đã được khởi tạo.

Vì vậy, nếu đối tượng của bạn cần xuất hiện dưới dạng một đối tượng khác hoặc hoạt động khác nhau tùy thuộc vào trạng thái hoặc điều kiện đối tượng, thì hãy sử dụng Thành phần : Tham khảo Mẫu thiết kế trạng thái và chiến lược .

Nếu đối tượng cần cùng loại, thì hãy sử dụng Giao diện kế thừa hoặc hiện thực.

35
Mecki 2013-02-01 09:34.

Cá nhân tôi học cách luôn thích sáng tác hơn là thừa kế. Không có vấn đề lập trình nào bạn có thể giải quyết với tính kế thừa mà bạn không thể giải quyết với thành phần; mặc dù bạn có thể phải sử dụng Giao diện (Java) hoặc Giao thức (Obj-C) trong một số trường hợp. Vì C ++ không biết bất kỳ điều gì như vậy, bạn sẽ phải sử dụng các lớp cơ sở trừu tượng, có nghĩa là bạn không thể loại bỏ hoàn toàn tính kế thừa trong C ++.

Thành phần thường logic hơn, nó cung cấp tính trừu tượng tốt hơn, đóng gói tốt hơn, sử dụng lại mã tốt hơn (đặc biệt là trong các dự án rất lớn) và ít có khả năng bị hỏng bất cứ thứ gì ở khoảng cách xa chỉ vì bạn đã thực hiện một thay đổi riêng biệt ở bất kỳ đâu trong mã của mình. Nó cũng giúp dễ dàng duy trì " Nguyên tắc trách nhiệm duy nhất ", thường được tóm tắt là " Không bao giờ được có nhiều hơn một lý do để một lớp thay đổi. ", Và điều đó có nghĩa là mọi lớp đều tồn tại cho một mục đích cụ thể và nó phải chỉ có các phương pháp liên quan trực tiếp đến mục đích của nó. Ngoài ra, việc có một cây thừa kế rất nông giúp cho việc giữ cái nhìn tổng quan dễ dàng hơn nhiều ngay cả khi dự án của bạn bắt đầu trở nên thực sự lớn. Nhiều người nghĩ rằng thừa kế đại diện cho thế giới thực của chúng ta khá tốt, nhưng đó không phải là sự thật. Thế giới thực sử dụng nhiều thành phần hơn là thừa kế. Hầu hết mọi vật thể trong thế giới thực mà bạn có thể cầm trong tay đều được cấu tạo từ các vật thể khác trong thế giới thực nhỏ hơn.

Tuy nhiên, có những mặt trái của thành phần. Nếu bạn hoàn toàn bỏ qua kế thừa và chỉ tập trung vào thành phần, bạn sẽ nhận thấy rằng bạn thường phải viết thêm một vài dòng mã không cần thiết nếu bạn đã sử dụng kế thừa. Đôi khi bạn cũng buộc phải lặp lại chính mình và điều này vi phạm Nguyên tắc KHÔ (DRY = Không lặp lại chính mình). Ngoài ra, thành phần thường yêu cầu ủy quyền và một phương thức chỉ gọi một phương thức khác của một đối tượng khác mà không có mã nào khác xung quanh cuộc gọi này. Những "lệnh gọi phương thức kép" như vậy (có thể dễ dàng mở rộng thành các lệnh gọi phương thức gấp ba hoặc gấp bốn và thậm chí xa hơn thế) có hiệu suất kém hơn nhiều so với kế thừa, nơi bạn chỉ đơn giản là thừa kế một phương thức của cha mẹ của mình. Việc gọi một phương thức kế thừa có thể nhanh như gọi một phương thức không kế thừa hoặc có thể chậm hơn một chút, nhưng thường vẫn nhanh hơn hai lần gọi phương thức liên tiếp.

Bạn có thể nhận thấy rằng hầu hết các ngôn ngữ OO không cho phép đa kế thừa. Mặc dù có một số trường hợp mà đa thừa kế thực sự có thể mua cho bạn thứ gì đó, nhưng đó là những trường hợp ngoại lệ hơn là quy luật. Bất cứ khi nào bạn rơi vào tình huống mà bạn nghĩ rằng "đa kế thừa sẽ là một tính năng thực sự thú vị để giải quyết vấn đề này", bạn thường ở thời điểm mà bạn nên nghĩ lại hoàn toàn về kế thừa, vì thậm chí nó có thể yêu cầu thêm một vài dòng mã , một giải pháp dựa trên thành phần thường sẽ trở thành bằng chứng thanh lịch, linh hoạt và trong tương lai hơn nhiều.

Tính năng kế thừa thực sự là một tính năng thú vị, nhưng tôi e rằng nó đã bị lạm dụng quá mức trong vài năm qua. Mọi người coi tài sản thừa kế như một chiếc búa có thể đóng đinh tất cả, bất kể nó thực sự là một chiếc đinh, một chiếc đinh vít hay có thể là một thứ hoàn toàn khác.

33
Boris Dalstein 2016-06-04 12:23.

Khi nào bạn có thể sử dụng thành phần?

Bạn luôn có thể sử dụng thành phần. Trong một số trường hợp, cũng có thể kế thừa và có thể dẫn đến một API trực quan và mạnh mẽ hơn, nhưng thành phần luôn là một tùy chọn.

Khi nào bạn có thể sử dụng quyền thừa kế?

Người ta thường nói rằng nếu "một thanh là một foo", thì lớp Barcó thể kế thừa lớp Foo. Rất tiếc, chỉ riêng bài kiểm tra này không đáng tin cậy, hãy sử dụng cách sau:

  1. một thanh là một foo,
  2. thanh có thể làm mọi thứ mà foos có thể làm.

Các thử nghiệm đảm bảo đầu tiên mà tất cả các thu khí của Foocó ý nghĩa trong Bar(= thuộc tính chia sẻ), trong khi thử nghiệm thứ hai làm cho chắc chắn rằng tất cả các setters của Foocó ý nghĩa trong Bar(chức năng = chia sẻ).

Ví dụ: Dog / Animal

Chó là động vật VÀ chó có thể làm mọi thứ mà động vật có thể làm (chẳng hạn như thở, di chuyển, v.v.). Do đó, lớp Dog có thể kế thừa lớp Animal.

Ví dụ đối chiếu: Hình tròn / Hình elip

Hình tròn là hình elip NHƯNG hình tròn không thể làm mọi thứ mà hình elip có thể làm được. Ví dụ: hình tròn không thể kéo dài, trong khi hình elip thì có thể. Do đó, lớp Circle không thể kế thừa lớp Ellipse.

Đây được gọi là vấn đề Circle-Ellipse , đây không thực sự là một vấn đề, nhưng nhiều hơn một dấu hiệu cho thấy "một thanh là một foo" không phải là một bài kiểm tra đáng tin cậy. Đặc biệt, ví dụ này nhấn mạnh rằng các lớp dẫn xuất nên mở rộng chức năng của các lớp cơ sở, không bao giờ hạn chế nó. Nếu không, lớp cơ sở không thể được sử dụng đa hình. Việc thêm kiểm tra "các thanh có thể làm mọi thứ mà foos có thể làm" đảm bảo rằng việc sử dụng đa hình là có thể và tương đương với Nguyên tắc thay thế Liskov :

Các hàm sử dụng con trỏ hoặc tham chiếu đến các lớp cơ sở phải có thể sử dụng các đối tượng của các lớp dẫn xuất mà không cần biết

Khi nào bạn nên sử dụng kế thừa?

Ngay cả khi bạn có thể sử dụng kế thừa không có nghĩa là bạn nên làm : sử dụng thành phần luôn là một lựa chọn. Kế thừa là một công cụ mạnh mẽ cho phép sử dụng lại mã ngầm và điều phối động, nhưng nó có một vài nhược điểm, đó là lý do tại sao bố cục thường được ưu tiên hơn. Sự cân bằng giữa kế thừa và thành phần không rõ ràng, và theo tôi, tốt nhất nên giải thích trong câu trả lời của lcn .

Theo nguyên tắc chung, tôi có xu hướng chọn kế thừa hơn thành phần khi việc sử dụng đa hình được cho là rất phổ biến, trong trường hợp đó sức mạnh của điều phối động có thể dẫn đến một API dễ đọc và thanh lịch hơn nhiều. Ví dụ: có một lớp đa hình Widgettrong các khung GUI hoặc một lớp đa hình Nodetrong các thư viện XML cho phép có một API dễ đọc và dễ sử dụng hơn nhiều so với những gì bạn sẽ có với một giải pháp hoàn toàn dựa trên thành phần.

25
Peter Tseng 2012-05-21 15:59.

Nguyên tắc chung của tôi: Trước khi sử dụng tính năng thừa kế, hãy cân nhắc xem bố cục có hợp lý hơn không.

Lý do: Phân lớp thường có nghĩa là phức tạp và liên kết hơn, tức là khó thay đổi, bảo trì và mở rộng quy mô mà không mắc lỗi.

Một câu trả lời đầy đủ và cụ thể hơn nhiều từ Tim Boudreau của Sun:

Các vấn đề thường gặp đối với việc sử dụng quyền thừa kế như tôi thấy là:

  • Các hành vi vô tội có thể có kết quả không mong muốn - Ví dụ cổ điển về điều này là các cuộc gọi tới các phương thức có thể ghi đè từ phương thức khởi tạo lớp cha, trước khi các trường cá thể của lớp con được khởi tạo. Trong một thế giới hoàn hảo, không ai có thể làm điều đó. Đây không phải là một thế giới hoàn hảo.
  • Nó cung cấp những cám dỗ tai hại cho các lớp con để đưa ra các giả định về thứ tự của các lệnh gọi phương thức và như vậy - các giả định như vậy có xu hướng không ổn định nếu lớp cha có thể phát triển theo thời gian. Xem sự tương tự máy nướng bánh mì và bình cà phê của tôi .
  • Các lớp trở nên nặng hơn - bạn không nhất thiết phải biết lớp cha của mình đang thực hiện công việc gì trong hàm tạo của nó, hoặc dung lượng bộ nhớ mà nó sẽ sử dụng. Vì vậy, việc xây dựng một số vật thể nhẹ vô tội có thể tốn kém hơn nhiều so với bạn nghĩ và điều này có thể thay đổi theo thời gian nếu lớp siêu cấp phát triển
  • Nó khuyến khích sự bùng nổ của các lớp con . Việc tải lớp tốn thời gian, nhiều lớp hơn tốn bộ nhớ. Điều này có thể không phải là vấn đề cho đến khi bạn đang xử lý một ứng dụng trên quy mô của NetBeans, nhưng ở đó, chúng tôi đã gặp vấn đề thực sự với, ví dụ, các menu chậm vì màn hình đầu tiên của menu đã kích hoạt tải lớp lớn. Chúng tôi đã sửa lỗi này bằng cách chuyển sang nhiều cú pháp khai báo hơn và các kỹ thuật khác, nhưng điều đó cũng tốn thời gian để sửa.
  • Việc thay đổi mọi thứ sau này trở nên khó khăn hơn - nếu bạn đã đặt một lớp ở chế độ công khai, việc hoán đổi lớp cha sẽ phá vỡ các lớp con - đó là một lựa chọn mà khi bạn đã công khai mã, bạn sẽ kết hôn. Vì vậy, nếu bạn không thay đổi chức năng thực cho lớp cha của mình, bạn sẽ có nhiều quyền tự do hơn để thay đổi mọi thứ sau này nếu bạn sử dụng, thay vì mở rộng thứ bạn cần. Lấy ví dụ, phân lớp con JPanel - điều này thường sai; và nếu lớp con được công khai ở đâu đó, bạn sẽ không bao giờ có cơ hội xem lại quyết định đó. Nếu nó được truy cập dưới dạng JComponent getThePanel (), bạn vẫn có thể làm điều đó (gợi ý: hiển thị các mô hình cho các thành phần bên trong dưới dạng API của bạn).
  • Cấu trúc phân cấp đối tượng không mở rộng quy mô (hoặc làm cho chúng mở rộng quy mô sau khó hơn nhiều so với việc lập kế hoạch trước) - đây là vấn đề cổ điển "quá nhiều lớp". Tôi sẽ đi sâu vào vấn đề này bên dưới và cách mô hình AskTheOracle có thể giải quyết nó (mặc dù nó có thể gây khó chịu cho những người theo chủ nghĩa OOP).

...

Ý kiến ​​của tôi về những gì phải làm, nếu bạn cho phép thừa kế, điều mà bạn có thể nhận với một hạt muối là:

  • Không để lộ trường nào, ngoại trừ hằng số
  • Các phương pháp phải trừu tượng hoặc cuối cùng
  • Không gọi phương thức nào từ hàm tạo lớp cha

...

tất cả những điều này ít áp dụng cho các dự án nhỏ hơn các dự án lớn và ít áp dụng cho các lớp tư nhân hơn các dự án công

19
yukondude 2008-09-08 16:29.

Sự kế thừa rất mạnh mẽ, nhưng bạn không thể ép buộc nó (xem: bài toán hình tròn-elip ). Nếu bạn thực sự không thể chắc chắn hoàn toàn về mối quan hệ kiểu phụ "là một" thực sự, thì tốt nhất bạn nên đi với bố cục.

16
simplfuzz 2010-11-30 00:54.

Giả sử một chiếc máy bay chỉ có hai phần: động cơ và cánh.
Sau đó, có hai cách để thiết kế một lớp máy bay.

Class Aircraft extends Engine{
  var wings;
}

Bây giờ máy bay của bạn có thể bắt đầu với việc có cánh cố định
và thay đổi chúng thành cánh quay khi đang bay. Về cơ bản nó
là một động cơ có cánh. Nhưng nếu tôi cũng muốn thay đổi
động cơ đang bay thì sao?

Lớp cơ sở để Enginelộ một trình đột biến để thay đổi các
thuộc tính của nó hoặc tôi thiết kế lại Aircraftthành:

Class Aircraft {
  var wings;
  var engine;
}

Bây giờ, tôi cũng có thể thay thế động cơ của mình khi đang bay.

15
egaga 2009-05-21 22:11.

Kế thừa tạo ra mối quan hệ chặt chẽ giữa lớp con và lớp siêu; lớp con phải biết chi tiết triển khai của lớp siêu. Tạo siêu lớp khó hơn nhiều, khi bạn phải nghĩ về cách nó có thể được mở rộng. Bạn phải ghi lại các bất biến của lớp một cách cẩn thận và nêu rõ các phương thức có thể ghi đè khác sử dụng nội bộ nào.

Sự kế thừa đôi khi hữu ích, nếu hệ thống phân cấp thực sự đại diện cho mối quan hệ là mối quan hệ. Nó liên quan đến Nguyên tắc Đóng mở, nói rằng các lớp nên được đóng để sửa đổi nhưng mở để mở rộng. Bằng cách đó, bạn có thể có tính đa hình; để có một phương thức chung xử lý siêu kiểu và các phương thức của nó, nhưng thông qua điều phối động, phương thức của lớp con được gọi. Điều này là linh hoạt và giúp tạo ra sự chuyển hướng, điều cần thiết trong phần mềm (để biết ít hơn về chi tiết triển khai).

Tuy nhiên, thừa kế dễ bị lạm dụng và tạo ra sự phức tạp bổ sung, với sự phụ thuộc cứng giữa các lớp. Ngoài ra, việc hiểu những gì xảy ra trong quá trình thực thi một chương trình trở nên khá khó khăn do các lớp và lựa chọn động của các cuộc gọi phương thức.

Tôi sẽ đề nghị sử dụng soạn thảo làm mặc định. Nó mang tính mô-đun hơn và mang lại lợi ích của việc ràng buộc muộn (bạn có thể thay đổi thành phần một cách linh hoạt). Ngoài ra, việc kiểm tra các thứ riêng biệt cũng dễ dàng hơn. Và nếu bạn cần sử dụng một phương thức từ một lớp, bạn không bị buộc phải ở dạng nhất định (Nguyên tắc thay thế Liskov).

13
nabeelfarid 2010-10-06 01:57.

Bạn cần phải xem Nguyên tắc thay thế Liskov trong nguyên tắc RẮN của Uncle Bob về thiết kế lớp. :)

8
Anzurio 2009-05-07 15:43.

Khi bạn muốn "sao chép" / phơi bày API của lớp cơ sở, bạn sử dụng tính năng kế thừa. Khi bạn chỉ muốn "sao chép" chức năng, hãy sử dụng ủy quyền.

Một ví dụ về điều này: Bạn muốn tạo một Ngăn xếp ngoài Danh sách. Stack chỉ có pop, push và peek. Bạn không nên sử dụng tính năng thừa kế vì bạn không muốn loại chức năng push_back, push_front, removeAt, v.v. trong ngăn xếp.

7
Tero Tolonen 2013-04-17 10:15.

Hai cách này có thể sống tốt với nhau và thực sự hỗ trợ lẫn nhau.

Composition chỉ chơi nó theo mô-đun: bạn tạo giao diện tương tự như lớp cha, tạo đối tượng mới và ủy quyền các lệnh gọi cho nó. Nếu những đối tượng này không cần biết về nhau, nó khá an toàn và dễ sử dụng. Có rất nhiều thú có túi ở đây.

Tuy nhiên, nếu lớp cha vì một lý do nào đó cần truy cập các chức năng do "lớp con" cung cấp cho lập trình viên thiếu kinh nghiệm, nó có thể giống như một nơi tuyệt vời để sử dụng kế thừa. Lớp cha chỉ có thể gọi nó là "foo ()" trừu tượng của riêng nó được ghi đè bởi lớp con và sau đó nó có thể cung cấp giá trị cho cơ sở trừu tượng.

Có vẻ như đây là một ý tưởng hay, nhưng trong nhiều trường hợp, tốt hơn hết chỉ nên cung cấp cho lớp một đối tượng thực thi foo () (hoặc thậm chí đặt giá trị được cung cấp theo cách thủ công foo ()) hơn là kế thừa lớp mới từ một số lớp cơ sở yêu cầu hàm foo () được chỉ định.

Tại sao?

Bởi vì kế thừa là một cách di chuyển thông tin kém .

Thành phần có một lợi thế thực sự ở đây: mối quan hệ có thể được đảo ngược: "lớp cha" hoặc "công nhân trừu tượng" có thể tổng hợp bất kỳ đối tượng "con" cụ thể nào triển khai giao diện nhất định + bất kỳ phần tử con nào có thể được đặt bên trong bất kỳ loại cha nào khác, chấp nhận đó là loại . Và có thể có bất kỳ số lượng đối tượng nào, ví dụ MergeSort hoặc QuickSort có thể sắp xếp bất kỳ danh sách đối tượng nào triển khai một Giao diện So sánh trừu tượng. Hay nói một cách khác: bất kỳ nhóm đối tượng nào triển khai "foo ()" và nhóm đối tượng khác có thể sử dụng các đối tượng có "foo ()" đều có thể chơi cùng nhau.

Tôi có thể nghĩ ra ba lý do thực sự để sử dụng tính năng thừa kế:

  1. Bạn có nhiều lớp với giao diện giống nhau và bạn muốn tiết kiệm thời gian viết chúng
  2. Bạn phải sử dụng cùng một Lớp cơ sở cho mỗi đối tượng
  3. Bạn cần sửa đổi các biến riêng tư, không thể công khai trong mọi trường hợp

Nếu những điều này là đúng, thì có lẽ cần phải sử dụng kế thừa.

Không có gì xấu khi sử dụng lý do 1, đó là điều rất tốt khi có một giao diện vững chắc trên các đối tượng của bạn. Điều này có thể được thực hiện bằng cách sử dụng thành phần hoặc kế thừa, không có vấn đề gì - nếu giao diện này đơn giản và không thay đổi. Thông thường kế thừa khá hiệu quả ở đây.

Nếu lý do là số 2 thì hơi khó. Bạn có thực sự chỉ cần sử dụng cùng một lớp cơ sở? Nói chung, chỉ sử dụng cùng một lớp cơ sở là không đủ, nhưng nó có thể là một yêu cầu của khung công tác của bạn, một cân nhắc thiết kế không thể tránh khỏi.

Tuy nhiên, nếu bạn muốn sử dụng các biến riêng, trường hợp 3, thì bạn có thể gặp rắc rối. Nếu bạn coi các biến toàn cục là không an toàn, thì bạn nên cân nhắc sử dụng kế thừa để truy cập vào các biến riêng cũng không an toàn . Xin lưu ý bạn, các biến toàn cục không phải là tất cả đều xấu - cơ sở dữ liệu về cơ bản là một tập hợp lớn các biến toàn cục. Nhưng nếu bạn có thể xử lý nó, thì nó khá ổn.

7
Scott Hannen 2016-05-14 10:24.

Để giải quyết câu hỏi này từ một góc độ khác cho các lập trình viên mới hơn:

Kế thừa thường được dạy sớm khi chúng ta học lập trình hướng đối tượng, vì vậy nó được xem như một giải pháp dễ dàng cho một vấn đề chung.

Tôi có ba lớp đều cần một số chức năng chung. Vì vậy, nếu tôi viết một lớp cơ sở và để tất cả chúng kế thừa từ nó, thì tất cả chúng sẽ có chức năng đó và tôi chỉ cần duy trì nó ở một nơi duy nhất.

Nghe có vẻ tuyệt vời, nhưng trong thực tế, nó hầu như không bao giờ hoạt động, vì một trong số các lý do:

  • Chúng tôi phát hiện ra rằng có một số chức năng khác mà chúng tôi muốn các lớp của mình có. Nếu cách chúng ta thêm chức năng vào các lớp là thông qua kế thừa, chúng ta phải quyết định - chúng ta có thêm nó vào lớp cơ sở hiện có không, mặc dù không phải mọi lớp kế thừa từ nó đều cần chức năng đó? Chúng ta có tạo một lớp cơ sở khác không? Nhưng còn các lớp đã kế thừa từ lớp cơ sở khác thì sao?
  • Chúng tôi phát hiện ra rằng đối với chỉ một trong các lớp kế thừa từ lớp cơ sở của chúng tôi, chúng tôi muốn lớp cơ sở hoạt động hơi khác một chút. Vì vậy, bây giờ chúng ta quay lại và sửa đổi lớp cơ sở của mình, có thể thêm một số phương thức ảo, hoặc thậm chí tệ hơn, một số mã có nội dung "Nếu tôi được thừa kế kiểu A, hãy làm điều này, nhưng nếu tôi được thừa kế kiểu B, hãy làm điều đó . " Điều đó thật tệ vì rất nhiều lý do. Một là mỗi khi chúng tôi thay đổi lớp cơ sở, chúng tôi đang thay đổi một cách hiệu quả mọi lớp kế thừa. Vì vậy, chúng tôi thực sự đang thay đổi lớp A, B, C và D bởi vì chúng ta cần một hành vi hơi khác ở lớp A. Hãy cẩn thận như chúng tôi nghĩ, chúng tôi có thể phá vỡ một trong những lớp đó vì những lý do không liên quan gì đến các lớp học.
  • Chúng tôi có thể biết lý do tại sao chúng tôi quyết định làm cho tất cả các lớp này kế thừa lẫn nhau, nhưng nó có thể không (có thể sẽ không) có ý nghĩa đối với người khác phải duy trì mã của chúng tôi. Chúng tôi có thể buộc họ vào một lựa chọn khó khăn - tôi có làm điều gì đó thực sự xấu xí và lộn xộn để thực hiện thay đổi mà tôi cần (xem phần gạch đầu dòng trước) hay tôi chỉ viết lại một đống điều này.

Cuối cùng, chúng tôi buộc mã của mình trong một số nút thắt khó khăn và không nhận được lợi ích nào từ nó ngoại trừ việc chúng tôi phải nói, "Tuyệt vời, tôi đã học về kế thừa và bây giờ tôi đã sử dụng nó." Điều đó không có nghĩa là trịch thượng bởi vì tất cả chúng ta đã làm điều đó. Nhưng tất cả chúng tôi đã làm điều đó bởi vì không ai bảo chúng tôi không làm vậy.

Ngay sau khi ai đó giải thích "thành phần ưu tiên hơn kế thừa" cho tôi, tôi đã nghĩ lại mỗi lần tôi cố gắng chia sẻ chức năng giữa các lớp bằng cách sử dụng kế thừa và nhận ra rằng hầu hết thời gian nó không thực sự hoạt động tốt.

Thuốc giải độc là Nguyên tắc Trách nhiệm Đơn lẻ . Hãy coi đó là một ràng buộc. Lớp tôi phải làm một việc. Tôi phải có thể đặt tên cho lớp của mình bằng cách nào đó mô tả một điều mà nó làm. (Có những ngoại lệ cho mọi thứ, nhưng các quy tắc tuyệt đối đôi khi tốt hơn khi chúng ta đang học.) Sau đó là tôi không thể viết một lớp cơ sở được gọi ObjectBaseThatContainsVariousFunctionsNeededByDifferentClasses. Bất kỳ chức năng riêng biệt nào tôi cần đều phải nằm trong lớp riêng của nó, và sau đó các lớp khác cần chức năng đó có thể phụ thuộc vào lớp đó, không kế thừa từ lớp đó.

Có nguy cơ đơn giản hóa quá mức, đó là thành phần - tổng hợp nhiều lớp để làm việc cùng nhau. Và một khi chúng ta hình thành thói quen đó, chúng ta thấy rằng nó linh hoạt, dễ bảo trì và có thể kiểm tra hơn nhiều so với việc sử dụng kế thừa.

6
Jon Limjap 2008-09-08 17:00.

Bên cạnh một / có một sự cân nhắc, người ta cũng phải xem xét "chiều sâu" kế thừa mà đối tượng của bạn phải trải qua. Bất cứ điều gì vượt quá năm hoặc sáu cấp độ kế thừa sâu có thể gây ra sự cố đúc và quyền anh / mở hộp không mong muốn và trong những trường hợp đó, thay vào đó, bạn nên soạn đối tượng của mình.

6
Amir Aslam 2013-12-30 15:57.

Khi bạn có một quan hệ is-a giữa hai lớp (ví dụ chó là một con chó), bạn sẽ kế thừa.

Mặt khác, khi bạn có một hoặc một số quan hệ tính từ giữa hai lớp học (học sinh có các khóa học) hoặc (giáo viên nghiên cứu các khóa học), bạn đã chọn thành phần.

5
Y.S 2014-06-05 01:36.

Một cách đơn giản để hiểu điều này là kế thừa nên được sử dụng khi bạn cần một đối tượng thuộc lớp của mình có cùng giao diện với lớp cha của nó, để do đó nó có thể được coi là một đối tượng của lớp cha (upcasting) . Hơn nữa, các lệnh gọi hàm trên một đối tượng lớp dẫn xuất sẽ vẫn giống nhau ở mọi nơi trong mã, nhưng phương thức cụ thể để gọi sẽ được xác định trong thời gian chạy (nghĩa là việc triển khai cấp thấp khác nhau, giao diện cấp cao vẫn như cũ).

Thành phần nên được sử dụng khi bạn không cần lớp mới có cùng giao diện, tức là bạn muốn che giấu một số khía cạnh của việc triển khai lớp mà người dùng của lớp đó không cần biết. Vì vậy, thành phần có nghĩa là hỗ trợ đóng gói (nghĩa là che giấu việc triển khai) trong khi kế thừa có nghĩa là hỗ trợ trừu tượng (tức là cung cấp một biểu diễn đơn giản hóa của một cái gì đó, trong trường hợp này là cùng một giao diện cho một loạt các loại có nội dung khác nhau).

4
Shelby Moore III 2011-12-02 21:40.

Kiểu phụ là thích hợp và mạnh mẽ hơn khi các bất biến có thể được liệt kê , nếu không thì sử dụng thành phần hàm để mở rộng.

4
Parag 2011-12-08 00:40.

Tôi đồng ý với @Pavel, khi anh ấy nói, có những nơi để sáng tác và có những nơi để kế thừa.

Tôi nghĩ kế thừa nên được sử dụng nếu câu trả lời của bạn là khẳng định cho bất kỳ câu hỏi nào trong số này.

  • Lớp của bạn có phải là một phần của cấu trúc được hưởng lợi từ tính đa hình không? Ví dụ: nếu bạn có một lớp Shape, khai báo một phương thức gọi là draw (), thì rõ ràng chúng ta cần các lớp Circle và Square là các lớp con của Hình dạng, để các lớp khách hàng của chúng sẽ phụ thuộc vào Hình dạng chứ không phụ thuộc vào các lớp con cụ thể.
  • Lớp của bạn có cần sử dụng lại bất kỳ tương tác cấp cao nào được xác định trong lớp khác không? Các phương pháp mẫu mẫu thiết kế sẽ không thể thực hiện mà không cần thừa kế. Tôi tin rằng tất cả các khuôn khổ có thể mở rộng đều sử dụng mẫu này.

Tuy nhiên, nếu ý định của bạn hoàn toàn là sử dụng lại mã, thì bố cục rất có thể là một lựa chọn thiết kế tốt hơn.

4
Enrique Molinari 2013-09-01 03:41.

Kế thừa là một cơ chế rất mạnh để tái sử dụng mã. Nhưng cần sử dụng đúng cách. Tôi sẽ nói rằng kế thừa được sử dụng đúng cách nếu lớp con cũng là một kiểu con của lớp cha. Như đã đề cập ở trên, Nguyên tắc thay thế Liskov là điểm mấu chốt ở đây.

Lớp con không giống với kiểu con. Bạn có thể tạo các lớp con không phải là kiểu con (và đây là lúc bạn nên sử dụng bố cục). Để hiểu kiểu con là gì, chúng ta hãy bắt đầu giải thích kiểu là gì.

Khi chúng ta nói rằng số 5 là kiểu số nguyên, chúng ta đang nói rằng 5 thuộc về một tập hợp các giá trị có thể có (ví dụ: hãy xem các giá trị có thể cho các kiểu nguyên thủy Java). Chúng tôi cũng cho biết rằng có một tập hợp các phương pháp hợp lệ mà tôi có thể thực hiện trên giá trị như cộng và trừ. Và cuối cùng chúng tôi nói rằng có một tập hợp các thuộc tính luôn được thỏa mãn, ví dụ, nếu tôi thêm các giá trị 3 và 5, tôi sẽ nhận được kết quả là 8.

Để đưa ra một ví dụ khác, hãy nghĩ về các kiểu dữ liệu trừu tượng, Tập hợp các số nguyên và Danh sách các số nguyên, các giá trị mà chúng có thể giữ được giới hạn ở các số nguyên. Cả hai đều hỗ trợ một tập hợp các phương thức, như add (newValue) và size (). Và cả hai đều có các thuộc tính khác nhau (lớp bất biến), Bộ không cho phép trùng lặp trong khi Danh sách cho phép trùng lặp (tất nhiên có các thuộc tính khác mà cả hai đều thỏa mãn).

Kiểu con cũng là một kiểu, có mối quan hệ với kiểu khác, được gọi là kiểu mẹ (hay siêu kiểu). Kiểu con phải thỏa mãn các tính năng (giá trị, phương thức và thuộc tính) của kiểu mẹ. Mối quan hệ có nghĩa là trong bất kỳ ngữ cảnh nào mà supertype được mong đợi, nó có thể được thay thế bởi một subtype, mà không ảnh hưởng đến hành vi thực thi. Hãy đi xem một số mã để minh họa những gì tôi đang nói. Giả sử tôi viết Danh sách các số nguyên (bằng một số loại ngôn ngữ giả):

class List {
  data = new Array();

  Integer size() {
    return data.length;
  }

  add(Integer anInteger) {
    data[data.length] = anInteger;
  }
}

Sau đó, tôi viết Tập hợp các số nguyên dưới dạng lớp con của Danh sách các số nguyên:

class Set, inheriting from: List {
  add(Integer anInteger) {
     if (data.notContains(anInteger)) {
       super.add(anInteger);
     }
  }
}

Lớp Tập hợp các số nguyên của chúng ta là một lớp con của Danh sách các số nguyên, nhưng không phải là một kiểu con, do nó không thỏa mãn tất cả các tính năng của lớp Danh sách. Các giá trị và chữ ký của các phương thức được thỏa mãn nhưng các thuộc tính thì không. Hành vi của phương thức add (Integer) đã được thay đổi rõ ràng, không bảo toàn các thuộc tính của kiểu cha. Hãy suy nghĩ từ quan điểm của khách hàng của các lớp học của bạn. Họ có thể nhận được Tập hợp các số nguyên trong đó Danh sách các số nguyên được mong đợi. Khách hàng có thể muốn thêm giá trị và thêm giá trị đó vào Danh sách ngay cả khi giá trị đó đã tồn tại trong Danh sách. Nhưng cô ấy sẽ không nhận được hành vi đó nếu giá trị tồn tại. Một bất ngờ lớn cho cô ấy!

Đây là một ví dụ kinh điển về việc sử dụng thừa kế không đúng cách. Sử dụng thành phần trong trường hợp này.

(một đoạn từ: sử dụng kế thừa đúng cách ).

3
Noname 2008-09-08 16:14.

Một quy tắc ngón tay cái mà tôi đã nghe là kế thừa nên được sử dụng khi quan hệ "is-a" và thành phần của nó khi là "has-a". Ngay cả với điều đó, tôi cảm thấy rằng bạn nên luôn nghiêng về bố cục vì nó loại bỏ rất nhiều sự phức tạp.

2
Shami Qureshi 2014-03-18 23:39.

Sáng tác v / s Thừa kế là một chủ đề rộng. Không có câu trả lời thực sự cho điều gì tốt hơn vì tôi nghĩ tất cả phụ thuộc vào thiết kế của hệ thống.

Nói chung kiểu quan hệ giữa các đối tượng cung cấp thông tin tốt hơn để chọn một trong số chúng.

Nếu kiểu quan hệ là quan hệ "IS-A" thì Kế thừa là cách tiếp cận tốt hơn. nếu không loại quan hệ là quan hệ "HAS-A" thì thành phần sẽ tiếp cận tốt hơn.

Nó hoàn toàn phụ thuộc vào mối quan hệ thực thể.

2
Veverke 2015-04-17 02:41.

Tôi thấy không ai đề cập đến vấn đề kim cương , vấn đề có thể nảy sinh với tài sản thừa kế.

Trong nháy mắt, nếu các lớp B và C kế thừa A và cả hai phương thức ghi đè X và lớp thứ tư D, kế thừa từ cả B và C, và không ghi đè X, thì việc triển khai XD sẽ sử dụng?

Wikipedia cung cấp một cái nhìn tổng quan tốt đẹp về chủ đề đang được thảo luận trong câu hỏi này.

2
Ravindra babu 2016-09-22 23:31.

Mặc dù Bố cục được ưu tiên hơn, nhưng tôi muốn nêu rõ ưu điểm của Tính kế thừa và nhược điểm của Bố cục .

Ưu điểm của Thừa kế:

  1. Nó thiết lập một quan hệ logic " LÀ A" . Nếu Ô tôXe tải là hai loại Phương tiện (hạng cơ sở), hạng con hạng cơ sở.

    I E

    Xe hơi là một phương tiện

    Xe tải là một phương tiện

  2. Với sự kế thừa, bạn có thể xác định / sửa đổi / mở rộng một khả năng

    1. Lớp cơ sở không cung cấp khả năng triển khai và lớp con phải ghi đè phương thức hoàn chỉnh (trừu tượng) => Bạn có thể triển khai hợp đồng
    2. Lớp cơ sở cung cấp triển khai mặc định và lớp con có thể thay đổi hành vi => Bạn có thể xác định lại hợp đồng
    3. Lớp con thêm phần mở rộng để triển khai lớp cơ sở bằng cách gọi super.methodName () làm câu lệnh đầu tiên => Bạn có thể gia hạn hợp đồng
    4. Lớp cơ sở xác định cấu trúc của thuật toán và lớp con sẽ ghi đè lên một phần của thuật toán => Bạn có thể triển khai Template_method mà không cần thay đổi trong khung lớp cơ sở

Nhược điểm của thành phần:

  1. Trong kế thừa, lớp con có thể gọi trực tiếp phương thức lớp cơ sở mặc dù nó không triển khai phương thức lớp cơ sở vì quan hệ IS A. Nếu bạn sử dụng thành phần, bạn phải thêm các phương thức trong lớp vùng chứa để hiển thị API lớp chứa

ví dụ: Nếu XeXe và nếu bạn phải lấy giá của Xe , đã được xác định trong Xe , mã của bạn sẽ như thế này

class Vehicle{
     protected double getPrice(){
          // return price
     }
} 

class Car{
     Vehicle vehicle;
     protected double getPrice(){
          return vehicle.getPrice();
     }
} 
1
LCJ 2012-08-02 01:15.

Như nhiều người đã nói, trước tiên tôi sẽ bắt đầu với việc kiểm tra - liệu có tồn tại mối quan hệ "là-một" hay không. Nếu nó tồn tại, tôi thường kiểm tra những điều sau:

Liệu lớp cơ sở có thể được khởi tạo hay không. Đó là, liệu lớp cơ sở có thể không trừu tượng hay không. Nếu nó có thể không trừu tượng, tôi thường thích bố cục hơn

Vd: 1. Kế toán Nhân viên. Nhưng tôi sẽ không sử dụng kế thừa vì một đối tượng Employee có thể được khởi tạo.

Ví dụ: 2. Sách là một Vật phẩm bán chạy. Một mục bán hàng không thể được khởi tạo - nó là một khái niệm trừu tượng. Do đó tôi sẽ sử dụng inheritacne. SellItem là một lớp cơ sở trừu tượng (hoặc giao diện trong C #)

Bạn nghĩ gì về cách làm này?

Ngoài ra, tôi ủng hộ câu trả lời @anon trong Tại sao sử dụng kế thừa?

Lý do chính để sử dụng kế thừa không phải là một dạng cấu thành - đó là vì vậy bạn có thể có được hành vi đa hình. Nếu bạn không cần tính đa hình, có lẽ bạn không nên sử dụng tính năng thừa kế.

@MatthieuM. nói tronghttps://softwareengineering.stackexchange.com/questions/12439/code-smell-inheritance-abuse/12448#comment303759_12448

Vấn đề với thừa kế là nó có thể được sử dụng cho hai mục đích trực giao:

giao diện (cho đa hình)

triển khai (để sử dụng lại mã)

TÀI LIỆU THAM KHẢO

  1. Thiết kế lớp nào tốt hơn?
  2. Kế thừa và Tổng hợp

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

Tận dụng lợi thế của việc bán hàng nhân Ngày của Cha này

Tận dụng lợi thế của việc bán hàng nhân Ngày của Cha này

Màn hình Samsung Galaxy S9 Plus. Chủ nhật này là Ngày của Cha⁠ — trong trường hợp nó khiến bạn suy nghĩ — và thay vì mua cho anh ấy một chiếc cà vạt trong năm nay, có lẽ đã đến lúc bạn mua cho anh ấy thứ mà anh ấy sẽ thực sự sử dụng.

Assassin's Creed Snuck Into Monster Hunter: World Last Night

Assassin's Creed Snuck Into Monster Hunter: World Last Night

Monster Hunter: World yêu thích các sự kiện chéo. Dù có nghĩa là hóa trang thành Dante của Devil May Cry, giả dạng Horizon: Zero Dawn's Aloy, hay chiến đấu với quái vật Final Fantasy, các nhiệm vụ sự kiện khác nhau của Thế giới được nâng cấp tự do so với các trò chơi khác.

Ava DuVernay Có Quà Tặng Ngày Của Mẹ cho Tất Cả Chúng Ta: Nếp Nhăn Thời Gian Sẽ Được Viết Lại Vào Cuối Tuần Ngày Của Mẹ!

Ava DuVernay Có Quà Tặng Ngày Của Mẹ cho Tất Cả Chúng Ta: Nếp Nhăn Thời Gian Sẽ Được Viết Lại Vào Cuối Tuần Ngày Của Mẹ!

Storm Reid, Oprah Winfrey, Mindy Kaling, Reese Witherspoon và Ava DuVernay tại buổi chiếu đặc biệt của A Wrinkle in Time tại Nhà hát Walter Reade ở Thành phố New York vào ngày 7 tháng 3 năm 2018 “Ava rất mong được nói chuyện với bạn,” một trong những người của Array dư luận viên nói qua điện thoại. (Array là tập thể phân phối, nghệ thuật và vận động chính sách của Ava DuVernay tập trung vào các bộ phim của người da màu và phụ nữ.

Nhiệm vụ bất khả thi 5 sẽ khôi phục niềm tin của bạn trong phim hành động Tentpole

Nhiệm vụ bất khả thi 5 sẽ khôi phục niềm tin của bạn trong phim hành động Tentpole

Mission Impossible: Rogue Nation bắt đầu ở một cấp độ khác. Theo nghĩa đen.

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

McCain, người đang làm việc cho một album mới, lần đầu tiên bước vào vòng kết nối vào tối thứ Sáu ở Nashville

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Tôi viết như thế nào

Tôi viết như thế nào

Đối với tôi, mọi thứ là về dòng đầu tiên đó và nó sẽ đưa bạn đến đâu. Một số nhà văn bị điều khiển bởi cốt truyện, sự sắp xếp tinh tế của các quân cờ, trong khi những người khác bị lôi cuốn bởi một nhân vật và khả năng thực hiện một cuộc hành trình với một người bạn hư cấu mới.

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Language