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