Why inheritance has always been meaningless

Original author: Graham Lee
  • Transfer
There are three types of inheritance.

  1. Ontological inheritance indicates specialization: this thing is a specific kind of thing (a soccer ball is a sphere and has such a radius).
  2. Inheritance of an abstract data type indicates a substitution: this thing has the same properties as that thing, and such and such behavior (this is the principle of substituting Barbara Liskov).
  3. Inheritance of the implementation is related to code sharing: this thing takes some properties of that thing and redefines or complements them in this way. Inheritance in my article “On Inheritance” of this and only this type.

These are three different and often conflicting relationships. To demand any or even all presents no difficulties. But requiring one mechanism to support two or more of them means running into problems.

Often, for OOP inheritance, they give a counterexample of the relationship between a square and a rectangle. Geometrically, a square is a specialization of a rectangle: all squares are rectangles, but not all rectangles are squares. All s in the Square class are s rectangleswhose length is equal to the width. But in the type hierarchy, this relationship is the opposite: you can use the rectangle wherever the square is used (by specifying a rectangle with the same width and height), but you cannot use the square wherever the rectangle is used (for example, you cannot change the length and width).

Note that there is an incompatibility between the direction of inheritance of geometric properties and properties of the abstract data type in squares and rectangles. These two dimensions are completely unrelated to each other in any software implementation. We haven't said anything about implementation inheritance yet, so we haven't even considered writing a program.

Smalltalk and many later languages ​​use simple inheritance to inherit the implementation, because multiple inheritance is incompatible with it because of the diamond problem (traits provide a reliable way to declare incompatibility, leaving the solution to the problem as an exercise for the reader). On the other hand, simple inheritance is incompatible with ontological inheritance, since a square is both a rectangle and an equilateral polygon.

The Smalltalk Blue Book describes inheritance solely in terms of implementation inheritance:
“The subclass determines that all its instances will, with the exception of the explicit differences, be the same as instances of another class called its superclass.”
Note the missing detail: it is not mentioned that an instance of a subclass should be able to replace an instance of a superclass everywhere in the program; it is not mentioned that an instance of a subclass must satisfy all conceptual tests for an instance of its superclass.

Inheritance has never been a problem: the problem is trying to use one tree for three different concepts .

“Prefer structure instead of inheritance” is essentially to abandon implementation inheritance. We cannot understand how to make it work, so let’s completely abandon it: we’ll make sharing through delegation, not subclasses.

Eiffel and separate streamlined approaches to using languages ​​like Java strengthen the relation “inheritance is the creation of subtypes”, weakening the relation “inheritance is reuse” (if the same method appears twice in unrelated parts of the tree, you will have to live with it to preserve the property, that each subclass is a subtype of its parent). This is normal if you are not trying to simulate the problem area as well using the inheritance tree. Typically, OOP literature recommends this when it comes to problem-oriented design.

Types strengthen the relation “inheritance is specialization”, weakening the relation “inheritance is reuse” (if both supercategories produce the same property of an instance, then neither of them is inherited and you must register it yourself). This is normal if you are not trying to also consider subclasses as covariant subtypes of your superclasses, but OOP literature usually recommends this, mentioning Barbara Liskov's substitution principle and the fact that the type in the method signature means that type or any subclass .

I believe that the following should be written in the literature : "here are three types of inheritance, focus on one of them." I also believe that languagesmust support this (obviously Smalltalk, Ruby and their friends support this due to the absence of any type restrictions).

  • If I use inheritance for code sharing, you should not assume that my subclasses are subtypes at the same time.
  • If I use subtypes to strengthen interface contracts, I should not only be allowed to mark the class anywhere in the tree as a subtype of another class anywhere in the tree, but be required to do so. Again, you should not assume that my subclasses are subtypes at the same time.
  • If I need to specify conceptual specialization through classes, then this also should not imply compliance with the inheritance tree. I should not only be allowed to mark the class anywhere in the tree as a subset of another class anywhere in the tree, but be required to do so. Again, you should not assume that my subclasses are specializations at the same time.

Your distribution area model is not an object model. This is not an abstract data type model. And the object model is not a model of an abstract data type.

Now inheritance has become simple again.

Also popular now: