JavaRush /Java Blog /Random EN /Inheritance as a phenomenon
articles
Level 15

Inheritance as a phenomenon

Published in the Random EN group
To tell you the truth, I didn’t initially plan this article. I considered the issues that I want to discuss here trivial, not even worth mentioning. However, in the process of writing articles for this site, I raised a discussion of multiple inheritance in one of the forums. As a result, it turned out that most developers have a very vague understanding of inheritance. And, accordingly, he makes a lot of mistakes. Since inheritance is one of the most important features of OOP (if not the most important!), I decided to devote a separate article to this phenomenon. * * * First, I want to distinguish between two concepts - object and class. These concepts are constantly confused. Meanwhile, they are central to OOP. And, in my opinion, it is necessary to know the differences between them. So, the object. Basically, it's anything. Here is the cube. Wooden, blue. The length of the edge is 5 cm. This is an object. And there's a pyramid. Plastic, red. 10 cm rib. This is also an object. What do they have in common? Different sizes. Different shape. Different material. However, they have something in common. First of all, both the cube and the pyramid are regular polyhedra. Those. the sum of the number of vertices and the number of faces is 2 more than the number of edges. Further. Both shapes have faces, edges and vertices. Both figures have such a characteristic as rib size. Both shapes can be rotated. Both figures can be drawn. The last two properties are already behavior. And so on. Programming practice shows that it is much easier to operate with homogeneous objects than with heterogeneous ones. And since there is still something in common between these figures, there is a desire to somehow highlight this commonality. This is where the concept of class comes into play. So, the definition.
A class is a descriptor of the common properties of a group of objects. These properties can be both characteristics of objects (size, weight, color, etc.), and behaviors, roles, etc.
Comment. The word “all” (descriptor of all properties) was not spoken. Which means that any object can belong to several different classes. Inheritance as a phenomenon - 1 Let's take the same example with geometric shapes as a basis. The most general description is regular polyhedron . Regardless of the edge size, number of faces and vertices. The only thing we know is that this figure has vertices, edges and faces, and that the lengths of the edges are equal. Further. We can make the description more specific. Let's say we want to draw this polyhedron . Let us introduce such a concept as a drawn regular polyhedron . What do we need for drawing? Description of a general drawing method that does not depend on specific vertex coordinates. Perhaps the color of the object. Now let's introduce the classes Cube and Tetrahedron . Objects belonging to these classes are certainly regular polyhedra. The only difference is that the numbers of vertices, edges and faces are already strictly fixed for each of the new classes. Further, knowing the type of a specific figure, we can give a description of the drawing method. This means that any object of the Cube or Tetrahedron classes is also an object of the drawn regular polyhedron class . There is a hierarchy of classes. In this hierarchy we descend from the most general description to the most specific. Note that an object of any class also fits the description of any more general class in the hierarchy. This class relationship is called inheritance . Each child class inherits all the properties of the parent, more general, and (possibly) adds some of its own to these properties. Or it overrides some properties of the parent class. Here I want to quote from Gradi Bucha's classic book on object-oriented design:
Inheritance, therefore, defines an "is a" hierarchy among classes, in which subclass inherits from one or more superclasses. This is in fact the litmus test for inheritance. Given classes A and B, if A "is not a" kind of B, then A shouldn't be a subclass of B.
Translated it sounds like this:
Inheritance thus defines an "is" hierarchy between classes, in which a subclass inherits from one or more superclasses. This is, in fact, the defining test (literally, a litmus test, my note) for inheritance. If we have classes A and B, and if class A "is not" a variant of class B, then A must not be a subclass of B.
Those who have read this far will probably twirl their finger at their temple in bewilderment. The first thought is that this is trivial! This is true. But if you knew how many crazy inheritance hierarchies I've seen! In that discussion that I mentioned at the very beginning, one of the participants quite seriously inherited a tank from... a machine gun!!! For the simple reason that the tank HAS a machine gun. And this is the most common mistake. Inheritance is confused with aggregation - the inclusion of one object within another. The tank is not a machine gun, it contains one. And because of this error, most often there is a desire to use multiple inheritance. Let's now move directly to Java. What is there in terms of inheritance? There are two types of classes in the language - those capable of containing an implementation, and those unable to do so. The latter are called interfaces, although in essence they are completely abstract classes. Inheritance as a phenomenon - 2 So, the language allows you to inherit a class from another class that potentially contains an implementation. BUT ONLY FROM ONE! Let me explain why this was done. The point is that each implementation can only deal with its part - with those variables and methods that it knows about. And even if we inherit class C from A and B , then the method processA , inherited from class A, can only work with the internal variable a , because it knows nothing about b , just as it knows nothing about c and the method processC . Likewise the processB method can only work with the variable b. That is, in essence, the inherited parts are isolated. Class C can certainly work with them, but it can just as well work with these parts if they are simply included as part of it rather than inherited. However, there is another nuisance here, which is the overlap of names. If the methods processA and processB were named the same, say process, then what effect would calling the process method of class C have ? Which of the two methods would be called? Of course, C++ has controls in this situation, but this does not add harmony to the language. So, implementation inheritance does not provide advantages, but there are disadvantages. For this reason, this implementation inheritance in Java has been abandoned. However, the developers were left with such an option for multiple inheritance as inheritance from an interface. In Java terms, an interface implementation. What is the interface? A set of methods. (We are not currently considering the definition of constants in interfaces; more about that here .) What is a method? And a method, at its core, determines the behavior of an object. It is no coincidence that the name of almost every method contains an action - getXXX , drawXXX , countXXX , etc. And since an interface is a collection of methods, the interface is, in fact, a determinant of behavior . Another option for using an interface is to define the role of an object. Observer, Listener , etc. In this case, the method is actually the embodiment of a reaction to some external event. That is, again, behavior. An object can of course have several different behaviors. If it needs to be rendered, it is rendered. If he needs to be saved, he is saved. Well, etc. Accordingly, the ability to inherit from classes that define behavior is very, very useful. Likewise, an object can have several different roles. However, implementationbehavior is entirely on the conscience of the child class. Inheritance from an interface (its implementation) says that an object of this class should be able to do this and that. And HOW it does this is determined independently by each class implementing the interface. Let's return to errors in inheritance. My experience in developing various systems shows that having inheritance from interfaces, you can implement any system without using multiple implementation inheritance. And therefore, when I encounter complaints about the lack of multiple inheritance in the form in which it exists in C++, for me this is a sure sign of incorrect design. The most common mistake made is the one I already mentioned - inheritance is confused with aggregation. Sometimes this happens due to incorrect assumptions. Those. Take, for example, a speedometer, it is argued that speed can only be measured by measuring distance and time, after which the speedometer is successfully inherited from the ruler and the clock, thus becoming the ruler and the clock, according to the definition of inheritance. (My requests to measure the time with a speedometer were usually answered with jokes. Or they did not answer at all.) What is the mistake here? In the premise. The fact is that the speedometer does not measure time. And distances, by the way, too. The odometer, which is found in any speedometer, is a classic example of a second device in the same housing, i.e. aggregation. It is not needed to measure speed. It can be removed altogether - this will not affect the speed measurement in any way. Sometimes such mistakes are made deliberately. This is much worse. “Yes, I know it’s wrong, but it’s more convenient for me.” What could this lead to? But here's what: we will inherit a tank from a cannon and a machine gun. It's more convenient this way. As a result, the tank becomes a cannon and a machine gun. Next we will equip the plane with two machine guns and a cannon. What do we get? An aircraft with suspended weapons in the form of three tanks! Because there is DEFINITELY a person who, without understanding it, uses a tank as a machine gun. Exclusively according to the inheritance hierarchy. And he will be absolutely right, because the one who designed such a hierarchy made a mistake.
In general, I don’t really understand the “ it’s more convenient for me this way” approach. It’s convenient to write like a listener, and those who say basic grammar are kazly. I’m exaggerating, of course, but the main idea remains - in addition to momentary convenience, there is such a thing as literacy. This concept is defined based on a very large number of people's experience. In fact, this is what in English is called “best practice” - the best solution. And more often than not, solutions that seem simpler bring a lot of problems in the future.
This example, of course, is greatly exaggerated and therefore absurd. However, there are less obvious cases that nevertheless lead to catastrophic consequences. By inheriting from an object, rather than aggregating it, the developer gives anyone the ability to use the parent object's functionality directly. With all that it implies. Imagine that you have a class that works with a database, DBManager . You create another class that will work with your data using DBManager - DataManager . This class will perform data control, transformations, additional actions, etc. In general, a layer between the business layer and the base layer. If you inherit DataManager from DBManager, then anyone using it will have access to the database directly. And, therefore, he will be able to perform any actions bypassing control, transformations, etc. Okay, let’s assume that no one wants to cause intentional harm and direct actions will be competent. But! Let's assume that the base has changed. I mean, some principles of control or transformation have changed. DataManager has been changed. But the code that previously worked directly with the database will continue to work. They will most likely not remember him. The result will be an error of such a class that those who look for it will turn grey. It would never occur to anyone that they work with the database bypassing the DataManager. By the way, an example from real life. It took a VERY long time to find the error. Finally, I’ll say it again. Inheritance should ONLY be used when there is an "is" relationship. Because this is the very essence of inheritance - the ability to use objects of a child class as objects of a base class. If there is no “is” relationship between classes, there SHOULD NOT be inheritance!!! Never and under no circumstances. And even more so – just because it’s so convenient. Link to the original source: http://www.skipy.ru/philosophy/inheritance.html
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION