Diamond inheritance is a situation in object-oriented programming languages with support for multiple inheritance , when two classes B and C inherit from A , and class D inherits from both classes B and C With this inheritance scheme, ambiguity may arise: if a method of class D calls a method defined in class A (and this method was not overridden in class D ), and classes B and C redefine this method in their own way, then from which class to inherit it: B or C ?
For example, in the area of developing graphical interfaces, the Button class can simultaneously inherit from the Rectangle class (Rectangle, for appearance) and the Clickable class (Accessible for clicking with the mouse, to implement functionality / input processing), and Rectangle and Clickable inherit from the Object class. If you call the equals method for the Button object and the Button class doesn’t have such a method, but the Object class contains the equals method in its own way redefined both in the Rectangle class and in Clickable , which method should be called up?
The diamond problem got its name from the outline of the class inheritance diagram in this situation. In this article, class A denoted as a vertex, classes B and C are separately indicated below, and D connected to both at the very bottom, forming a rhombus .
Content
Solutions
Various programming languages solve the problem of diamond-shaped inheritance in the following ways:
- C ++ does not create rhomboid inheritance by default: the compiler processes each inheritance path separately, as a result of which the
Dobject will actually contain two different subobjectsA, and when using members ofAwill need to specify the inheritance path (B::AorC::A) . To generate a rhomboid inheritance structure, you need to use virtual inheritance of classAon several inheritance paths: if both inheritance fromAtoBand fromAtoCare marked with thevirtualspecifier (for example,class B : virtual public A), C ++ in a special way will track the creation only one subobjectA, and using members ofAwill work correctly. If the virtual and non-virtual inheritance are mixed, then one virtual subobjectAobtained, and one non-virtual subobjectAfor each non-virtual inheritance path toAIn a virtual method call of a virtual base class, the so-called dominance rule is used: the compiler prohibits a virtual method call that was overloaded on several inheritance paths. - Common Lisp tries to implement both reasonable default behavior and the ability to change it. By default, the method with the most specific argument classes is selected; then, the methods are selected in the order in which the parent classes are specified when defining the subclass. However, the programmer may well change this behavior by specifying a special order for resolving methods or specifying a rule for combining methods.
- Eiffel handles this situation with
selectandrename, and the ancestor methods that are used in descendants are explicitly specified. This allows you to share the methods of the parent class in descendants or provide them with a separate copy of the parent class. - Perl and Io handle in-depth inheritance in the order used in the class definition. Class
Band its ancestors will be checked against classCand its ancestors, so that the method inAwill be inherited fromB; permission list - [D,B,A,C]. At the same time, in Perl this behavior can be changed usingmroor other modules to apply C3-linearization (as in Python) or other algorithms. - In Python, the diamond problem became acute in version 2.3 after introducing classes with a common ancestor
object; starting with this version, it was decided to create a permission list using C3-linearization [1] . In the case of a rhombus, this means searching in depth , starting from the left (D,B,A,C,A), and then removing from the list all but the last inclusion of each class that is repeated in the list. Therefore, the final resolution order looks like this: [D,B,C,A]. - Scala permission list is created similarly to Python, but through a search in depth starting from the right. Therefore, the preliminary resolution list of the rhombus is [
D,C,A,B,A], and after deleting the repetitions - [D,C,B,A]. - JavaFX Script , since version 1.2, allows multiple inheritance through the use of impurities . In the event of a conflict, the compiler prohibits the direct use of undefined variables or functions. Each inherited member will still be accessible by bringing the object to the desired impurity, for example,
(individual as Person).printInfo();.
Other examples.
Languages that allow only simple inheritance (such as Ada , Objective-C , PHP , C # , Delphi / Free Pascal and Java ) provide for multiple inheritance of interfaces (called protocols in Objective-C). Interfaces are essentially abstract base classes, all methods of which are also abstract, and where fields are missing. Thus, the problem does not arise, since there will always be only one implementation of a particular method or property, avoiding the occurrence of uncertainty.
The rhombus problem is not limited to inheritance. It also occurs in languages such as C and C ++ , when the header files A, B, C and D, as well as individual precompiled headers created from B and C, are connected (using the #include instruction) to each other in a diamond pattern, indicated at the top. If these two precompiled headers are combined, the declarations in A are duplicated and the #ifndef connection protection directive becomes ineffective. A problem is also found when combining stacks of subroutine software ; for example, if A is a database, and B and C are caches , then D can request both B and C to confirm ( COMMIT ) the transaction, leading to duplicate confirmation calls A.
Notes
- ↑ The Python 2.3 Method Resolution Order . Date of treatment May 15, 2010. Archived April 12, 2012.
Literature
- Eddy Truyen; Wouter Joosen, Bo Jørgensen, Petrus Verbaeten. A Generalization and Solution to the Common Ancestor Dilemma Problem in Delegation-Based Object Systems (English) // Proceedings of the 2004 Dynamic Aspects Workshop: journal. - 2004. - No. 103-119 .