Is OOP & completely avoiding implementation inheritance possible?


I will choose Java as an example, most people know it, though every other OO language was working as well.

我會選擇Java作為一個例子,大多數人都知道它,盡管其他所有OO語言都可以正常工作。

Java, like many other languages, has interface inheritance and implementation inheritance. E.g. a Java class can inherit from another one and every method that has an implementation there (assuming the parent is not abstract) is inherited, too. That means the interface is inherited and the implementation for this method as well. I can overwrite it, but I don't have to. If I don't overwrite it, I have inherited the implementation.

與許多其他語言一樣,Java具有接口繼承和實現繼承。例如。 Java類可以從另一個繼承,並且每個在其中具有實現的方法(假設父級不是抽象的)也是繼承的。這意味着接口是繼承的,也是此方法的實現。我可以覆蓋它,但我沒有。如果我不覆蓋它,我繼承了實現。

However, my class can also "inherit" (not in Java terms) just an interface, without implementation. Actually interfaces are really named that way in Java, they provide interface inheritance, but without inheriting any implementation, since all methods of an interface have no implementation.

但是,我的類也可以“繼承”(不是用Java術語)只是一個接口,沒有實現。實際上接口實際上是用Java命名的,它們提供了接口繼承,但沒有繼承任何實現,因為接口的所有方法都沒有實現。

Now there was this article, saying it's better to inherit interfaces than implementations, you may like to read it (at least the first half of the first page), it's pretty interesting. It avoids issues like the fragile base class problem. So far this makes all a lot of sense and many other things said in the article make a lot of sense to me.

現在有這篇文章,說繼承接口比實現更好,你可能想讀它(至少在第一頁的前半部分),這很有意思。它避免了脆弱的基類問題等問題。到目前為止,這很有意義,文章中說的許多其他內容對我來說很有意義。

What bugs me about this, is that implementation inheritance means code reuse, one of the most important properties of OO languages. Now if Java had no classes (like James Gosling, the godfather of Java has wished according to this article), it solves all problems of implementation inheritance, but how would you make code reuse possible then?

對此有什么不妥,是實現繼承意味着代碼重用,這是OO語言最重要的屬性之一。現在,如果Java沒有類(如James Gosling,Java的教父希望根據這篇文章),它解決了實現繼承的所有問題,但是如何使代碼重用成為可能呢?

E.g. if I have a class Car and Car has a method move(), which makes the Car move. Now I can sub-class Car for different type of cars, that are all cars, but are all specialized versions of Car. Some may move in a different way, these need to overwrite move() anyway, but most would simply keep the inherited move, as they move alike just like the abstract parent Car. Now assume for a second that there are only interfaces in Java, only interfaces may inherit from each other, a class may implement interfaces, but all classes are always final, so no class can inherit from any other class.

例如。如果我有一個類Car和Car有一個方法move(),這會讓Car移動。現在我可以為不同類型的汽車分類汽車,這些都是汽車,但都是汽車的專用版本。有些可能以不同的方式移動,這些都需要覆蓋move(),但大多數只會保留繼承的移動,因為它們像抽象的父Car一樣移動。現在假設在Java中只有接口,只有接口可以相互繼承,一個類可以實現接口,但所有類都是final,所以沒有類可以從任何其他類繼承。

How would you avoid that when you have an Interface Car and hundred Car classes, that you need to implement an identical move() method for each of them? What concepts for code reuse other than implementation inheritance exist in the the OO world?

如果你有一個Interface Car和100個Car類,你需要為每個類實現相同的move()方法嗎? OO世界中存在除了實現繼承之外的代碼重用的哪些概念?

Some languages have Mixins. Are Mixins the answer to my question? I read about them, but I cannot really imagine how Mixins would work in a Java world and if they can really solve the problem here.

有些語言有Mixins。 Mixins是我問題的答案嗎?我讀到了它們,但我無法想象Mixins如何在Java世界中運行,以及它們是否真的可以在這里解決問題。

Another idea was that there is a class that only implements the Car interface, let's call it AbstractCar, and implements the move() method. Now other cars implement the Car interface as well, internally they create an instance of AbstractCar and they implement their own move() method by calling move() on their internal abstract Car. But wouldn't this be wasting resources for nothing (a method calling just another method - okay, JIT could inline the code, but still) and using extra memory for keeping internal objects, you wouldn't even need with implementation inheritance? (after all every object needs more memory than just the sum of the encapsulated data) Also isn't it awkward for a programmer to write dummy methods like

另一個想法是,有一個類只實現Car接口,讓我們稱之為AbstractCar,並實現move()方法。現在其他汽車也實現了Car接口,在內部他們創建了一個AbstractCar實例,他們通過在內部抽象Car上調用move()來實現自己的move()方法。但這不會浪費資源(一種方法只調用另一種方法 - 好吧,JIT可以內聯代碼,但仍然)並使用額外的內存來保存內部對象,你甚至不需要實現繼承? (畢竟每個對象都需要更多的內存,而不僅僅是封裝數據的總和)對於程序員來說,編寫虛擬方法也不是很尷尬

public void move() {
    abstractCarObject.move();
}

?

Anyone can imagine a better idea how to avoid implementation inheritance and still be able to re-use code in an easy fashion?

任何人都可以想象一個更好的想法如何避免實現繼承,仍然能夠以簡單的方式重用代碼?

8 个解决方案

#1


11  

Short answer: Yes it is possible. But you have to do it on purpose and no by chance ( using final, abstract and design with inheritance in mind, etc. )

簡短的回答:是的,這是可能的。但是你必須故意這樣做而不是偶然(使用最終的,抽象的和帶有繼承的設計等)

Long answer:

答案很長:

Well, inheritance is not actually for "code re-use", it is for class "specialization", I think this is a misinterpretation.

那么,繼承實際上不是為了“代碼重用”,而是為了“類專業化”,我認為這是一種誤解。

For instance is it a very bad idea to create a Stack from a Vector, just because they are alike. Or properties from HashTable just because they store values. See [Effective].

例如,從Vector創建堆棧是一個非常糟糕的主意,因為它們是相似的。或者來自HashTable的屬性只是因為它們存儲了值。見[有效]。

The "code reuse" was more a "business view" of the OO characteristics, meaning that you objects were easily distributable among nodes; and were portable and didn't not have the problems of previous programming languages generation. This has been proved half rigth. We now have libraries that can be easily distributed; for instance in java the jar files can be used in any project saving thousands of hours of development. OO still has some problems with portability and things like that, that is the reason now WebServices are so popular ( as before it was CORBA ) but that's another thread.

“代碼重用”更像是OO特征的“業務視圖”,這意味着您的對象可以在節點之間輕松分發;並且是可移植的,並且沒有以前編程語言生成的問題。事實已證明這一點很嚴重。我們現在擁有可以輕松分發的庫;例如,在java中,jar文件可以在任何項目中使用,從而節省數千小時的開發時間。 OO仍然存在一些可移植性等問題,這就是現在WebServices如此受歡迎的原因(就像之前的CORBA一樣),但這是另一個線程。

This is one aspect of "code reuse". The other is effectively, the one that has to do with programming. But in this case is not just to "save" lines of code and creating fragile monsters, but designing with inheritance in mind. This is the item 17 in the book previously mentioned; Item 17: Design and document for inheritance or else prohibit it. See [Effective]

這是“代碼重用”的一個方面。另一個是有效的,與編程有關的那個。但在這種情況下,不僅要“保存”代碼行並創建脆弱的怪物,還要考慮繼承設計。這是前面提到的書中的第17項;第17項:繼承的設計和文件,或禁止它。見[有效]

Of course you may have a Car class and tons of subclasses. And yes, the approach you mention about Car interface, AbstractCar and CarImplementation is a correct way to go.

當然,你可能有一個Car類和大量的子類。是的,你提到的關於Car interface,AbstractCar和CarImplementation的方法是正確的方法。

You define the "contract" the Car should adhere and say these are the methods I would expect to have when talking about cars. The abstract car that has the base functionality that every car but leaving and documenting the methods the subclasses are responsible to handle. In java you do this by marking the method as abstract.

你定義了汽車應該遵守的“合同”,並說這些是我在談論汽車時所期望的方法。具有基本功能的抽象汽車,每輛汽車都離開並記錄子類負責處理的方法。在java中,您可以通過將方法標記為抽象來完成此操作。

When you proceed this way, there is not a problem with the "fragile" class ( or at least the designer is conscious or the threat ) and the subclasses do complete only those parts the designer allow them.

當你以這種方式進行時,“脆弱”類(或者至少設計者是有意識的或威脅的)沒有問題,並且子類只完成設計者允許的那些部分。

Inheritance is more to "specialize" the classes, in the same fashion a Truck is an specialized version of Car, and MosterTruck an specialized version of Truck.

繼承更多的是“專門化”類,以同樣的方式,Truck是Car的專用版本,MosterTruck是Truck的專用版本。

It does not make sanse to create a "ComputerMouse" subclase from a Car just because it has a Wheel ( scroll wheel ) like a car, it moves, and has a wheel below just to save lines of code. It belongs to a different domain, and it will be used for other purposes.

因為它有一個像汽車一樣的輪子(滾輪),它會移動,並且下方有一個輪子只是為了保存代碼行,所以它不會讓汽車從汽車中創建一個“ComputerMouse”子類酶。它屬於不同的域,它將用於其他目的。

The way to prevent "implementation" inheritance is in the programming language since the beginning, you should use the final keyword on the class declaration and this way you are prohibiting subclasses.

防止“實現”繼承的方法是從編程語言開始,你應該在類聲明中使用final關鍵字,這樣就禁止了子類。

Subclassing is not evil if it's done on purpose. If it's done uncarefully it may become a nightmare. I would say that you should start as private and "final" as possible and if needed make things more public and extend-able. This is also widely explained in the presentation"How to design good API's and why it matters" See [Good API]

如果它是故意的,那么子類化並不是邪惡的。如果做得不好,可能會成為一場噩夢。我會說你應該盡可能私密和“最終”開始,如果需要的話,可以讓事情變得更加公開和可擴展。這在演示文稿“如何設計好API及其重要性”中也得到了廣泛的解釋。請參閱[Good API]

Keep reading articles and with time and practice ( and a lot of patience ) this thing will come clearer. Although sometime you just need to do the work and copy/paste some code :P . This is ok, as long you try to do it well first.

繼續閱讀文章,隨着時間和練習(和很多耐心),這件事情會變得更加清晰。雖然有時您只需要完成工作並復制/粘貼一些代碼:P。這是好的,只要你先嘗試做好。

Here are the references both from Joshua Bloch ( formerly working in Sun at the core of java now working for Google )

以下是Joshua Bloch的參考資料(以前在Sun工作,現在是Google的核心工作)


[Effective] Effective Java. Definitely the best java book a non beginner should learn, understand and practice. A must have.

Effective Java

有效的Java


[Good API]Presentation that talks on API's design, reusability and related topics. It is a little lengthy but it worth every minute.

How To Design A Good API and Why it Matters

如何設計一個好的API及其重要性

Regards.

問候。


Update: Take a look at minute 42 of the video link I sent you. It talks about this topic:

"When you have two classes in a public API and you think to make one a subclass of another, like Foo is a subclass of Bar, ask your self , is Every Foo a Bar?... "

“如果你在公共API中有兩個類,並且你想讓一個類成為另一個類的子類,比如Foo是Bar的子類,那么問問你的自己,Every Foo是吧?......”

And in the minute previous it talks about "code reuse" while talking about TimeTask.

在前一分鍾,它在討論TimeTask時討論“代碼重用”。

#2


8  

The problem with most example against inheritance are examples where the person is using inheritance incorrectly, not a failure of inheritance to correctly abstract.

大多數針對繼承的示例的問題是人正在使用繼承錯誤的示例,而不是繼承失敗以正確抽象。

In the article you posted a link to, the author shows the "brokenness" of inheritance using Stack and ArrayList. The example is flawed because a Stack is not an ArrayList and therefore inheritance should not be used. The example is as flawed as String extending Character, or PointXY extending Number.

在您發布鏈接的文章中,作者使用Stack和ArrayList顯示了繼承的“破壞性”。該示例存在缺陷,因為Stack不是ArrayList,因此不應使用繼承。該示例與String擴展字符或PointXY擴展Number一樣有缺陷。

Before you extend class, you should always perform the "is_a" test. Since you can't say Every Stack is an ArrayList without being wrong in some way, then you should not inheirit.

在擴展類之前,應始終執行“is_a”測試。既然你不能說每個堆棧都是一個ArrayList而沒有在某種程度上出錯,那么你就不應該知道。

The contract for Stack is different than the contract for ArrayList (or List) and stack should not be inheriting methods that is does not care about (like get(int i) and add()). In fact Stack should be an interface with methods such as:

Stack的契約與ArrayList(或List)的契約不同,堆棧不應該是繼承不關心的方法(如get(int i)和add())。事實上,Stack應該是一個具有以下方法的接口:

interface Stack<T> {
   public void push(T object);
   public T pop();
   public void clear();
   public int size();
}

A class like ArrayListStack might implement the Stack interface, and in that case use composition (having an internal ArrayList) and not inheritance.

像ArrayListStack這樣的類可能會實現Stack接口,在這種情況下使用組合(具有內部ArrayList)而不是繼承。

Inheritance is not bad, bad inheritance is bad.

繼承不壞,壞繼承不好。

#3


4  

You could also use composition and the strategy pattern.link text

您還可以使用合成和策略模式。鏈接文本

public class Car
{
  private ICar _car;

  public void Move() {
     _car.Move();
  }
}

This is far more flexible than using inheritance based behaviour as it allows you to change at runtime, by substituting new Car types as required.

這比使用基於繼承的行為更靈活,因為它允許您在運行時更改,方法是根據需要替換新的Car類型。

#4


2  

You can use composition. In your example, a Car object might contain another object called Drivetrain. The car's move() method could simply call the drive() method of it's drivetrain. The Drivetrain class could, in turn, contain objects like Engine, Transmission, Wheels, etc. If you structured your class hierarchy this way, you could easily create cars which move in different ways by composing them of different combinations of the simpler parts (i.e. reuse code).

你可以使用作文。在您的示例中,Car對象可能包含另一個名為Drivetrain的對象。汽車的move()方法可以簡單地調用它的動力傳動系統的drive()方法。反過來,Drivetrain類可以包含Engine,Transmission,Wheels等對象。如果以這種方式構建類層次結構,您可以通過組合更簡單部分的不同組合來輕松創建以不同方式移動的汽車(即重用代碼)。

#5


2  

To make mixins/composition easier, take a look at my Annotations and Annotation Processor:

要使mixins / composition更容易,請查看我的Annotations和Annotation Processor:

http://code.google.com/p/javadude/wiki/Annotations

http://code.google.com/p/javadude/wiki/Annotations

In particular, the mixins example:

特別是mixins的例子:

http://code.google.com/p/javadude/wiki/AnnotationsMixinExample

http://code.google.com/p/javadude/wiki/AnnotationsMixinExample

Note that it doesn't currently work if the interfaces/types being delegated to have parameterized methods (or parameterized types on the methods). I'm working on that...

請注意,如果委托的接口/類型具有參數化方法(或方法上的參數化類型),則它當前不起作用。我正在研究......

#6


2  

It's funny to answer my own question, but here's something I found that is pretty interesting: Sather.

回答我自己的問題很有意思,但我發現這里的內容非常有趣:Sather。

It's a programming language with no implementation inheritance at all! It knows interfaces (called abstract classes with no implementation or encapsulated data), and interfaces can inherit of each other (actually they even support multiple inheritance!), but a class can only implement interfaces (abstract classes, as many as it likes), it can't inherit from another class. It can however "include" another class. This is rather a delegate concept. Included classes must be instantiated in the constructor of your class and are destroyed when your class is destroyed. Unless you overwrite the methods they have, your class inherits their interface as well, but not their code. Instead methods are created that just forward calls to your method to the equally named method of the included object. The difference between included objects and just encapsulated objects is that you don't have to create the delegation forwards yourself and they don't exist as independent objects that you can pass around, they are part of your object and live and die together with your object (or more technically spoken: The memory for your object and all included ones is created with a single alloc call, same memory block, you just need to init them in your constructor call, while when using real delegates, each of these objects causes an own alloc call, has an own memory block, and lives completely independently of your object).

它是一種完全沒有實現繼承的編程語言!它知道接口(稱為抽象類,沒有實現或封裝數據),接口可以相互繼承(實際上它們甚至支持多重繼承!),但是類只能實現接口(抽象類,盡可能多),它不能從另一個類繼承。然而,它可以“包括”另一個類。這是一個委托概念。必須在類的構造函數中實例化包含的類,並在銷毀類時銷毀它們。除非你覆蓋他們擁有的方法,否則你的類也會繼承它們的接口,但不會繼承它們的代碼。而是創建了一些方法,只是將對方法的調用轉發給包含對象的同名方法。包含對象和剛封裝的對象之間的區別在於,您不必自己創建委托轉發,它們不作為您可以傳遞的獨立對象存在,它們是您的對象的一部分,與您一起生活和死亡對象(或更具技術性的說法:對象的內存和所有包含的內存是使用單個alloc調用創建的,相同的內存塊,您只需要在構造函數調用中初始化它們,而在使用真實委托時,每個對象都會導致一個自己的alloc調用,有一個自己的內存塊,完全獨立於你的對象而生活)。

The language is not so beautiful, but I love the idea behind it :-)

語言並不那么美,但我喜歡它背后的想法:-)

#7


0  

You should read Design Patterns. You will find that Interfaces are critical to many types of useful Design Patterns. For example abstracting different types of network protocols will have the same interface (to the software calling it) but little code reuse because of different behaviors of each type of protocol.

你應該閱讀設計模式。您會發現接口對於許多類型的有用設計模式都至關重要。例如,抽象不同類型的網絡協議將具有相同的接口(對於調用它的軟件),但由於每種類型的協議的不同行為,所以很少代碼重用。

For some algorithms are eye opening in showing how to put together the myriad elements of a programming to do some useful task. Design Patterns do the same for objects.Shows you how to combine objects in a way to perform a useful task.

對於一些算法是開放的,展示如何將編程的無數元素組合起來做一些有用的任務。設計模式對對象執行相同的操作。向您展示如何以執行有用任務的方式組合對象。

Design Patterns by the Gang of Four

四人幫的設計模式

#8


0  

Inheritance is not necessary for an object oriented language.

面向對象語言不需要繼承。

Consider Javascript, which is even more object-oriented than Java, arguably. There are no classes, just objects. Code is reused by adding existing methods to an object. A Javascript object is essentially a map of names to functions (and data), where the initial contents of the map is established by a prototype, and new entries can be added to a given instance on the fly.

考慮一下Javascript,它可能比Java更面向對象。沒有類,只有對象。通過向對象添加現有方法來重用代碼。 Javascript對象本質上是函數(和數據)的名稱映射,其中映射的初始內容由原型建立,並且新條目可以動態添加到給定實例。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com