Inheritance (object-oriented programming)

The Inheritance ( Inheritance engl. ) is one of the fundamental concepts of object orientation, and has great importance in software development. The inheritance is used, building on existing classes to create new, and the relationship between the original and the new class is permanent. A new class can be an extension or a restriction of the original class. In addition to this constructive aspect of inheritance is also the documentation of similarities between classes, which is particularly important in the early stages of software design is important. On the heredity -based class hierarchies reflect structural and behavioral similarities of classes.

The inheriting class is usually called the base class (also super, top or parent class ), inheriting the derived class (also sub-, sub - or child class). The process of inheritance is called usually lead or specialization, the reversal thereof generalization, which is a predominantly limited to the model level term. In the Unified Modeling Language ( UML) an inheritance relationship is represented by an arrow with a triangular tip pointing from the derived class to the base class. Inherited attributes and methods will not be repeated in the representation of the derived class.

In the programming language Simula inheritance with other concepts of object-oriented programming was introduced in 1967 for the first time. The latter has since opened up important new perspectives in the software development and also allows the component-based development.

Derived class and base class are typically one another in a " is - a " relationship. Classes are the specification of the data type and functionality, both of which can be inherited. Some programming languages ​​separate, at least partially between these aspects and distinguish between interface (English Interface) and class. If a derived class from more than one base class inherits, this is called multiple inheritance. But multiple inheritance is not possible in any programming language, with some only in limited form.

  • 5.1 Multiple Inheritance
  • 5.2 Covariance and contravariance
  • 5.3 Data encapsulation in the context of inheritance
  • 5.4 Type conversion on static typing
  • 5.5 Programming with central base class
  • 5.6 Persistence in databases
  • 6.1 supplement or adapt a class interface
  • 6.2 Fragile Base Class Problem

Example

The following example shows a possible excerpt from the application of the support of a car rental company represents the base class vehicle containing the attributes of vehicle number, tare weight and permissible total weight. The specializations motor vehicle and bicycle add more attributes to the methods inherited from the base class weights and the identifying vehicle number. Each object of class motor vehicle so the attributes of vehicle number, tare weight, permissible total weight, top speed and performance are stored. In the class vehicle there is the method PruefeVerfuegbarkeit that determined using the vehicle number, the availability of a specific vehicle, for example by a database access. This method is inherited by all specializations and is therefore also used for this. The same is true even if subsequently another method in the class vehicle is supplemented, for example, a method HatNavigationssystem when part of the vehicles will be equipped with navigation systems.

In class motor vehicle license Check the method is introduced. This method is intended for passing in a concrete driving determine whether represented by an object of that class vehicle may only be performed with this license. [A 1] The license could be country-specific, in addition, countries must be taken into account, in which the motor vehicle is to be operated. Possibly some implementations can be made on the basis of the attributes speed and power already in the class motor vehicle, if the suitability of the license already granted in the gross vehicle weight, top speed and performance is decidable for all vehicles operated a country. Many cases are decidable but only at the level of specialization motorcycles, cars and trucks, so this method must be overridden in these classes. For example, the number of seats the vehicle has to be considered in some cases.

Within this model implemented the application program would be to check whether a driving license is valid, instantiates the concrete, to be rented vehicle by entering the appropriate vehicle data, ie the corresponding specialization.

In addition, an object of the present driving would also be generated. This would be passed to the method Valid driver's license of the vehicle object and evaluated by it. The outcome of the review could for example be displayed to the clerk. The structure of the memory map is shown in the adjacent diagram schematically for an object of class car. The inherited from the various classes of attributes typically lie directly behind the other. Furthermore, the memory image of an object contains a pointer to the so-called table of virtual methods that are used to determine the Einsprungspunkts the concrete implementation of an invocation.

Applications of inheritance

Inheritance allows for development of software modeling, which is very close to human perception of the real world. There are many different applications of the inheritance mechanism. It is still disputed whether the inheritance should be used only in very limited areas of application and whether a use with the main intention of reusing code of the software quality is rather detrimental.

The following application contexts are recommended or dive in practice:

  • Subtype Inheritance: In this the inheriting class is a subtype of the base class in the sense of an abstract data type. This means that an object of the subtype can be used in any location where an object of the base type is expected. The set of possible values ​​of the subtype represents a subset of those of the base type
  • Inheritance for extension: In the derived class, new functionality will be added over the base class. This variant of the inheritance is an apparent contradiction with the restrictive subtype inheritance represents the extension refers here but additional attributes. [A 2] This variant also includes adjustments by overriding methods, for example, to add functionality that is not in the base class is relevant. This variant also includes the case that only some of the functionality of an abstract class in the derived - in this case, too abstract - class is implemented, and in addition Reserved required implementations further specializations remain ( Reification ).
  • Inheritance to support general skills: In this variant it comes to establish the support of basic functionality of an application architecture or class library. A basic functionality such as serializability or comparability is thereby declared by an abstract class (interface) - Typical identifiers are Serializable and Comparable The implementation of all requirements of the interface must be in the derived class. Corresponds formally to this type of inheritance of subtype inheritance.
  • Inheritance of default implementations: General useful for several types of functionality is implemented here in central classes. This variant is the appropriate reuse of general program parts ( mixin class).

There are also uses of inheritance, which are not considered to be meaningful. In particular, in the first steps of object-oriented programming often results in an exaggerated enthusiasm resulting from the grading of the inheritance hierarchy, often for a simple additional property. For example, are likely to be useful and prevent the modeling of the actually relevant aspects rather for a Person class specializations WeiblichePerson and Males in most cases. Another questionable use is when the inheriting class not in a " is - a" - but in a " has " relationship is the base class, and an aggregation would be appropriate. This occurs frequently in association with multiple inheritance. Apple pie as the heir of cake and apple represents a prime example of this modeling error, since Apple is not a sensible base class.

In the transition from the object-oriented modeling to programming, there is the situation that the modeling of a classificatory hierarchy of professional application objects can not be transferred to the program level without further notice. For example, may seem useful as specializations of person from a conceptual point of view, modeling of customer and employee. On the programming level, such a classification to a static - that is, a person can not programmatically change readily from the role of the employee on the role of the customer. On the other hand, a person can in this way also does not play multiple roles simultaneously. The fact that the latter can not be meaningfully achieved by adding a multiple inheriting, further specialization KundeUndMitarbeiter, supplier becomes clear, for example, when adding a further role. The usual solution is to separate the aspects and the modeling of an associative relationship between person and their roles.

Variants of the inheritance of type and implementation

By means of inheritance can be passed to the derived class, both the type that is specified by its interface, as well as the functionality. The consequences of this dual function of inheritance has been controversial for years. Newer programming languages ​​such as Java or. NET languages ​​like C # and VB.NET do not support continuous separation of these variants inheritance, but offer for interfaces (interface ) and classes (class ) two formally separate concepts. We can distinguish three cases:

  • Inheritance of type and implementation ( mostly implementation inheritance or simply called inheritance, Eng. Subclassing )
  • Inheritance of type ( usually referred to as interface inheritance, Eng. Subtyping )
  • Pure inheritance of implementation ( in Java or. NET languages ​​not directly possible)

In the last variant derived class and base class are not in a " is - a " relationship.

Implementation inheritance

Here, the implementation and implicitly their interface is inherited from the base class. The derived class assumes the attributes and functionality of the base class and converts them as necessary from or supplemented this by a further specialization of these additional relevant properties. Even if it subsequently functionality of the base class increased or improved, benefiting the derived class thereof.

The following is an example of the derivation of the square is outlined as a specialization of rectangle in the Java programming language. This example can be found in similar form frequently in the literature and is for illustration of many - even unfavorable - Aspects helpful, but really can not be considered as a particularly good example of inheritance.

The rectangle class has the attributes len and wide, which are set via the constructor. In addition, there are methods (functions ) for the calculation of the scale and the length of the diagonals of the rectangle. The specialization square inherits this functionality ( extends keyword ). The constructor for square requires only one instead of two parameters, since length and width so agree. The interfaces implemented by the class rectangle calculations of scope and diagonal length vote for the square. A modification of the diagonal length of the calculation carried out, as they can be calculated in a square in a simpler manner - in this example, however, is by way of illustration - for reasons of optimization. The calculation of the scale is not reimplemented, but taken over by the base class - although of course there would also be possible, a slight simplification.

Public class Rectangle {      private double length;      private double width;        public Rectangle ( double length, double width)      {          this.breite = width;          this.laenge = len;      }        public double getLaenge () {return length; }      public double getBreite () {return width; }        public double getUmfang ()      {          return 2 * len 2 * width;      }        public double getDiagonale ()      {          return Math.sqrt ( len * len width * width);      } } public class Square extends Rectangle {      / / One-time calculation of the square root of 2      static final double WURZEL2 = Math.sqrt (2);        public square (int len)      {          / / Call the constructor of the base class          super ( len, len );      }        @ Override      public double getDiagonale ()      {          return WURZEL2 * getLaenge ();      } } Interface Inheritance

In software development, there have been since the 1970s, two parallel developments, one of which resulted in the object-oriented programming, on the other hand, algebraic specification methods to support software design have been developed. One advantage of such specifications is that they can be provided with a mathematical semantics. An important result of these efforts was the concept of abstract data type, which has the specification of a data type independent of the implementation to the target. Classes, strictly speaking, their interfaces are considered as the image of an abstract data type. But this is really inappropriate that when inheritance from virtually any language a consistent separation of the inheritance of interface and implementation is explicitly supported. Although relatively new languages ​​like Java and. NET languages ​​lead to the interfaces (interfaces ) is a concept for mapping of abstract data types, but do not support continuous separation, as is an interface once implemented by a class, inherits any further specialization both the implementation as the interface. Specialists for object-oriented programming, such as Bertrand Meyer see in a complete splitting more harm than good. One reason is that the near interface and implementation in the program code facilitating the understanding and the maintainability.

In this regard, attention is also the Liskov Substitution Principle. This requires that a subtype must behave so that somebody who thinks they have an object of the base type in front of him, is not surprised by unexpected behavior, if it is actually is an object of the subtype. Object-oriented programming languages ​​can a violation of this principle, which can occur due to the associated with the inheritance polymorphism, not excluded a priori. Often a violation of the principle is not obvious at first glance. If the methods are introduced setLaenge and setBreite about the example outlined above in the base class rectangle for the subsequent change in size [A 3], the square has to be decided in the class, how to deal with it. One possible solution is that the width is automatically set to the same value when setting the length and vice versa. If an application is assumed to have a square in front of him, and doubling the length of a rectangle a doubling of the area expected to be surprised when an instance of the type square caused by automatic alignment of width four times the area. [A 4]

The lack of separation between type and implementation inheritance in practice this often means that shine through in the interface of a class implementation details. One strategy to avoid this effect is the use of abstract classes or interfaces in the rhizosphere areas of the class hierarchy. It is advantageous to differentiate on an abstract level as far as possible before implementations are added. Based on such a foundation interface is necessary in conjunction with distributed architectures such as CORBA or COM.

Pure implementation inheritance

In the pure implementation inheritance, which is also referred to as private inheritance, the inheriting class uses the functionality and attributes of the base class without having to apply to the outside as a subclass of this class. As a - somewhat contrived - example, a class RechtwinkligesDreieck of the class rectangle in the above example could inherit the implementation to calculate the hypotenuse of the method getDiagonale after the length of the short sides of length and width were used.

For example, in C or Eiffel there is the possibility of a pure implementation inheritance in Java or. NET languages ​​, there is not. An alternative in the latter languages ​​is the use of delegation, which requires a lot more code.

Interaction of the methods in the class hierarchy

If a method is overridden in a class that is often only adds functionality and the implementation of the base class can still be used, since they are already covering general aspects, which are also valid for the specialization. Therefore it is necessary that within the method implementation of the specialized class, the counterpart of the base class is called. This call is typically done at the beginning or end of the overriding method, but partly also additional functionality to implement before and after this call.

The various programming languages ​​allow a call to the base class implementation in different ways. Most of degrees of freedom provides C , there is the method name, the class name as a prefix prepended (Scope operator). This process goes far beyond this application, because it enables you to call any method of all classes within the class hierarchy. Something is more restrictive as Java, where there are the super keyword, which precedes the method name. Significantly formal calling the base class method is solved, for example, in the language CLOS: There, the base implementation is called without method name or parameters are specified solely by the keyword call -next -method, the current parameters of the spezialisierenden method are implicitly passed. The formal approach is less redundant and error-prone, but it offers less flexibility.

Based on the introductory example can explain such a call cascade. The method PruefeFahrerlaubnis are thereby returned if the test could be carried out, and if this is the case, in addition, the result of this check. The implementation of the class car first calls the implementation of the class vehicle to abzuhandeln the cases that are decidable based on speed, performance, or gross vehicle weight. The implementation in motor vehicle turn delegated the audit of the total allowable weight on to its base class. After returning from the called base implementations, the test will continue from when the case has not yet been decided.

Special Features of inheritance

Multiple Inheritance

→ Main article: Multiple Inheritance

In order multiple inheritance is when a derived class inherits directly from more than one base class. A sequential, multi-stage heirs other hand, is not referred to as multiple inheritance. A very common use case of multiple inheritance is the use of mixin classes, which account for general-purpose implementations and thus help to avoid redundancy.

Another example of multiple inheritance is due to the expansion of the introductory example to the class rail car and two-way vehicle. The latter inherits both from motor vehicle as well as by rail car and thus have all of the attributes of motor vehicles, as well as the additional attribute gauge, which is inherited from rail vehicle.

The need for multiple inheritance is controversial, it is not supported by all languages ​​, for example, not of Smalltalk. The first language that supported multiple inheritance, was Flavors, an object- oriented extension of LISP. A comprehensive support offering for example, C , Eiffel, and Python. Java and. NET languages ​​provide a limited support, there can be a class, although any number of interfaces but only inherit from a class that contains implementations. Another solution keeps Ruby ready, there only one direct base class is also possible, but a class can incorporate any number of so-called modules, which is the basic idea of ​​a mixin inheritance directly.

Apart from a considerable additional implementation complexity for the compiler and runtime environment, there are mainly two reasons for the frequent lack of or limited support:

For erstgenanntes problem the languages ​​usually provide ways of renaming. The latter constellation, which is also called Diamond problem, makes its appearance only in inheritance of implementation. Here it can be both sensible that the resulting object contains only one instance of the class occur more than once, as well as several. For the above example of the two-way vehicle, this means either the presence of only one instance of the base class of the vehicle or of the two. C provides the concept of so-called virtual base classes to both possibilities. Eiffel also offers both opportunities and this even at the level of individual attributes and methods. This can even be useful in the example shown: The curb weight is at a two-way vehicle is basically the same, whether it is run on the track or on the road. However, this does not necessarily apply to the total weight. Python has implemented to generate a meaningful inheritance hierarchy since version 2.3, in such cases the concept of so-called C3 linearization.

Covariance and contravariance

→ Main article: covariance and contravariance

In connection with the substitution liskovschen principle is also the treatment of the variance in the signatures using prescribed methods. Many programming languages ​​do not allow for variance, that is, the types of the method parameters specified methods must match exactly. The liskovschen principle corresponds to the support of contravariance for input and output parameters for covariance. This means that input parameters can be more general than in the base class, the type of the return value must be special.

With few exceptions, the declaration of the languages ​​(English exceptions) it is possible that you may experience when calling a method. The types of possible exceptions to this include the signature of a method. For Java and Modula -3 - the only two known languages ​​that support such a thing - has the set of possible exception types an overridden method to be a subset of the original types, what covariance means and the liskovschen substitution principle corresponds to [A 5].

In connection with the liskovschen substitution principle is the design -by -contract approach, which is supported by Eiffel. It is possible to define pre-and postconditions for methods and invariants for classes. The class variants and post-conditions have to be equal to or more restrictive in specializations that preconditions can be relaxed.

Encapsulation in the context of inheritance

When specifying the visibility of the attributes and methods of classes ( encapsulation ) is often distinguish whether the access, for example, by a derived class or "outside", that is at a different use of the class. In most languages, three cases are distinguished:

  • (public ): The property is visible without restrictions
  • Protected (protected): The property is in the class itself and for derived classes visible ( in several stages ), from the outside is not.
  • Private ( private ): The property is visible only within the class itself.

Not all languages ​​support this three-part structure. Some definitions of visibility are also interpreted differently. Java and. NET languages ​​lead have variants, which limit the visibility of a language-specific subunits of the program structure (Package or Assembly). In Ruby has a private different meaning: On private properties can be accessed also by specializing classes. However, in principle, to access the properties the same instance possible. [A 6]

Another relevant aspect in inheritance of data encapsulation is the ability to change the visibility of properties compared to the base class in derived classes. For example, in C or Eiffel, it is possible to restrict the visibility of all or individual properties when inheriting. In Java or. NET languages ​​, however, no such change in visibility at inheritance is possible.

Type conversion on static typing

Programming languages ​​can be divided into those with static or dynamic typing. With dynamic typing, a type for variables and parameters not explicitly set. Smalltalk was the first object-oriented language with dynamic typing. In contrast, static typing is - usually by a declaration such as in Java - indicated what type must have the value of a variable or a parameter. In assignment or parameter passing the assignment compatibility of types can already be tested in this case during compilation.

At each point where an object of a particular class is expected also an object can be used, which belongs to a specialization of this class. For example, a variable of type cars are always assigned to a variable of type motor vehicle. However, after such allocation are the additional features of specialization, in the example, the number of seats, not directly accessible. However, the object of the base class behaves when calling virtual methods as an object of class spezialisierenden. [A 7] Such a conversion is called upcast.

The counterpart to this, a downcast is problematic, however, in some cases necessary. Even static typifying languages ​​usually allow such a conversion, which must be explicitly initiated but. In this case, checking system with static typifying languages ​​at runtime whether an object actually has the required type. Such a downcast, such as motor vehicle car is only useful if it is guaranteed that the object is actually the type of concrete specialization. If no check is performed and, in this example converts an object that represents a truck into the car type, usually an exception is generated.

Programming languages ​​with central base class

Many object-oriented programming languages ​​have a central class from which all classes - about how many steps whatsoever - are ultimately derived. For example, there is such a class in Ruby, Java and. NET. This is, in these languages ​​Object. In Eiffel it is called with ANY. Among the few exceptions where there is no such class, including C or Python.

In languages ​​with central base class inherits a class for which no base class is specified implicitly by this particular class. One advantage of this is that general functionality, for example for serialization or the type information can be accommodated there. Furthermore, it allows the declaration of variables, where an object of any class can be assigned. This is especially useful for implementing of container classes if a language does not support a generic programming. [A 8] However, this method has the disadvantage that such a general container objects of any type can be added. Since when accessing an object of the container usually a special type is expected, therefore, a type conversion ( Downcast ) is required. The corresponding type checking, however, can only be done at runtime.

Persistence in databases

In addition to a simple serialization to store in a database is the most common method to make objects persistent. Object-oriented databases have the goal to avoid a so-called impedance mismatch which occurs when the figure used in the programming inheritance and object structure to a relational database. Object-oriented databases but have not enforced until today, so often called object-relational mapper used.

In object- relational mapping of the inheritance relationships three options are available:

In the first variant (Single Table Inheritance ) the type of the object must be stored in an additional column. The column of the attributes that are not present in the concrete objects of the class hierarchy containing zero values ​​. Both are not necessary with the last two versions, the third version is a kind of compromise.

In true object-oriented databases are essentially two opposing strategies can be distinguished: Persistence by inheritance (by Inheritance ) and orthogonal persistence. The Persistence by inheritance the property to determine whether an object is transient or is persistent depends on the type, and is established by inheriting from a class that provides functionality for connecting to the database. In orthogonal persistence objects of the same class can be either persistent or transient, the property is therefore completely independent of the type.

Inheritance in the Context of Software Maintenance

Object-oriented elements and not least the inheritance mechanism have an expression, the very positive impact on the quality and comprehensibility of a system design. Extensive class libraries are developed, whose functionality can be application-specific adapted or extended by means of inheritance. Not least thanks to the inheritance mechanism software systems can be modular, allowing the controllability of large systems and for example also facilitates porting. However, unnecessarily deeply nested inheritance hierarchies increase the complexity and understanding greatly, which can lead to errors in use or modification of the base classes.

Apart from the positive aspects and negative aspects with regard to the software maintenance have been shown in object-oriented programming, which are mainly related to the polymorphism, but also with the inheritance.

Supplement or adapt a class interface

The most problematic case is the subsequent change in the interface of a central class of which there are numerous specializations, for example in connection with the conversion to a new version of a class library. Here mainly two cases must be distinguished:

If the new method is introduced without implementation in the first case, as part of an abstract class, all specializations version switch must now provide this functionality. Far more serious, however, if the inheritance hierarchy in subordinate classes there was already a virtual method of the same name. This case can not be detected by the compiler in most languages ​​. These existing virtual method will be called in a context for which it was not implemented. If this problem is not eliminated by means of the processing of the documentation of the version change, it results in incorrect system behavior and usually a runtime error.

In the second case, the renaming or signature adaptation in specializing classes must be retightened. If it does not, the previous implementations now hang " in the air", that is, they are no longer called in required places, an existing in a base class standard functionality is used instead, which actually provided customized functionality is not executed. This problem can not be detected by the compiler in some constellations.

Ensuring that such problems can be recognized by the compiler, actually requires a relatively small complement of a language. In C #, for example, this is covered by the override keyword. In all methods that override a virtual method of the base class, this keyword must be specified. The fact that in most languages ​​, as well as C or Java [A 9] lacking such support, is that this aspect was not sufficiently taken into account in the design of the language, and pushes the subsequent introduction of such a keyword, because of large compatibility issues with considerable resistance.

Fragile Base Class Problem

→ Main article: Fragile Base Class Problem

Even without the change of a class interface can occur on a new version of the base class for conversion to problems. The developer modifies a " fragile " base class, in this case are not in a position to anticipate the negative consequences that result for specialized classes by the change. The reasons for this are manifold, essentially is a misunderstanding between the developers of the base class and those of the specializations used before. This is mostly because the functionality of the base class and also the expected behavior of the specializations are not sufficiently precise specified.

A common cause of the Fragile Base Class problem is the overly generous disclosure of implementation details, which are usually done for practical reasons, whereby parts are disclosed which are not yet fully developed in an initial version. The programming languages ​​facilitate the implementation of meaningful restrictions on the degrees of freedom are often not, for example, methods are always virtual in Java and must be marked final, if not overwritten by a deriving class should be possible.

Inheritance in prototype- based programming

The concept of inheritance is also used for prototype-based programming. In prototype-based languages ​​, however, no distinction between class and object instantiiertem. Accordingly, it is meant here not quite the same with inheritance, since a signal generated by cloning a new object " inherits " not only the structure of the parent, also known as the original, but also the content. The mechanism for the use of the methods of the parent by the copy (Child ) actually corresponds to a delegation. This is useful in terms of transmission, but it has more degrees of freedom, for example, in some such languages ​​the addressee of the delegation - and thus the " base class " - runtime replaceable.

417974
de