07. Inheritance in OOP (Part 2)

Overlapping inheritance, dynamic inheritance, multi-aspect inheritance, and structural design patterns.

Educational outcomes

Student:

  • explains how to implement constructs such as: disjoint inheritance, abstract classes, overriding methods, abstract methods, overlapping, incomplete inheritance, multiinheritance in natural language (understanding);
  • identifies and corrects errors in the source code, which is an implementation of the above structure;
  • lists constructs implemented in the source code (understanding);
  • implements the above structures using the selected programming language (application);
  • indicates functional differences between two selected programs based on code snippets source (analysis);
  • creates a program that implements specific business requirements using the constructs learned from the lectures (synthesis);

Justification

Modern programming languages ​​are mostly based on the object paradigm. Therefore, its practical understanding is a necessary basis for working in this profession. Moreover, programmers often make systems based on analytical materials. And these largely use object-oriented graphical notations (such as UML). During this and the next lessons, you will deepen your theoretical bond, but first of all you will learn how to implement different concepts from object-orientness. This will allow you to design and implement modern business applications.

Multi-aspect inheritance

The types of inheritance discussed so far covered only one aspect (division criterion). In most cases this is sufficient, but not always. Let's go back to the previous example (specializations Persons: Employee, Student, Pensioner). Suppose we would like to include the gender of the people mentioned there. And depending on it, store some additional information and perform specific operations. For example, for women, this will be the maiden name and birth of the child, and for men the attitude to military service and going to the army. Let's see how we can accomplish this:

  • In the Person class put appropriate attributes (and for men and women) and methods. If the object will describe a man, then the "feminine" features will not be used, and vice versa. As easy to guess, this is not the best way.
  • Modify existing subclasses and add new ones, including gender: "Female Worker", "Male Worker", "Female Student", "Student Male", etc. And again tt's probably not the best idea: duplicate attributes and methods repeatedly. For our three classes and two genders this does not lead to a large number of combinations, but if we needed to divide the three categories, each of which has three options, then the number is going to be substantial.

As you can easily guess, we will be assisted by multi-aspect inheritance. Let's look at the figure below:

Multi-aspect inheritance

We have added two classes: Woman and Man. They contain the appropriate attributes and methods. The aspect names are placed at the inheritance sign (they are mandatory). Seeing such a diagram, we can expect objects belonging to the class, such as Female Employee, Male Pensioner, etc. The principle is simple: we take one class from every aspect (unless there is, for example, overlapping, then we can use any combination of classes from this aspect) and stick them together.

Unfortunately, this kind of generalization does not occur in any of the popular programming languages. Therefore, we need to implement them somehow, using the following suggestions:

  • One aspect is inherited using built-in (simple) inheritance mechanisms for a particular programming language.
  • Other aspects are implemented using one of the previously discussed ways.
  • We can also try to remove some of the hierarchy or, in other words, group in one class, eg by adding flags to the main class. So we can do it when we do not need to store different information depending on the value of this flag. In other words, this flag is simply a kind of discriminator.

The question immediately arises: what aspect should we inherit, and which one model in another way? As in the case of multiple inheritance, we leave intact this aspect which:

  • There is a method override and polymorphic call,
  • There is a greater diversity of attributes in each subclass. In other words - the most complicated/advanced hierarchy.

In some situations when we do not store information that is specific to a particular aspect, and only the object type information, we can replace the inheritance with, for example, a flag located in the superclass. In such a situation, it may be better to place one or two attributes in the superclass, even if they are not always used, rather than complicate the model with eg a composition.

Knowledge Check
Select all that apply

Which statements are true?

Examples of multi-aspect inheritance realization

Let's look at the examples. The following figure shows one possible solution to the problem with multi-aspect inheritance. The idea is to put attributes and methods from the removed aspect to the superclass. It is worth noting the notation of the attributes. Both maiden name, and attitude to military service are marked as optional. This is because, depending on the sex of the person, one of them will have no value.

multi-aspect inheritance - example no 1

Another solution for the same initial diagram is shown below. Instead of the subclass Woman and Man we have the composition and classes Female and Male. Note that class semantics have also changed. Sex is not a person (like a class previously), but only a certain set of information about it. Of course, you can discuss whether this change was needed. In my opinion yes, but if someone did not do it, then I would not consider it a mistake. Just in this type of a case, many designers have so many solutions. It is also worth noting that thanks to the cardinality of composition equal to 1 we will always be connected to exactly one object storing such information. And unlike the previous approach, this time no attribute is unnecessarily occupying space. The price for this is probably a little more complicated diagram, and what is usually followed by a more difficult implementation.

multi-aspect inheritance - example no 2
Knowledge Check
Select all that apply

Which statements are true?

Dynamic inheritance - flattening hierarchy

Dynamic inheritance is quite unusual. So far as the object was created as Employee, it also ended its computer life as Employee. This is not the most convenient approach. In life it happens that someone is eg a student, later the same person is an employee and then, for example, a pensioner. Since we are modeling real business situations, it would be good to have a mechanism that allows it. And that's what dynamic inheritance is for. Let's look at the diagram below. Stereotype <<dynamic>> means what we mean: dynamic inheritance, that is, subclass objects can freely change their membership while the system is running.

Dynamic inheritance

Like most of its predecessors, it does not exist in C++, nor in C#, nor in Java. Traditionally, we will have to implement them somehow manually using different techniques.

One is to place all invariants in the superclass and add the discriminator. We used this method in other types of inheritance, so we will not discuss it here in detail. Actually, this approach has only one advantage: easy implementation. The rest is probably only disadvantages.

Knowledge Check
Select all that apply

Which statements are true?

Dynamic inheritance - composition

One way to implement dynamic inheritance is to use aggregation/composition with the constraint {xor} (example shown below). We use the code created in case of the overlapping inheritance. In addition, we have included methods to facilitate the "change of class". Their task is to remove the existing object-part (responsible for the "dynamic aspect" of the object) and create a new one. For the example this could be a transforming worker into a pensioner or a student into an employee. These methods are placed in the object-whole (class Person).

Dynamic inheritance - composition
Knowledge Check
Select all that apply

Which statements are true?

Dynamic inheritance - copying

"Smart" copying objects. The idea is to replace the old object belonging to the original class, which is an instance of a new class - this "after the transformation". To do this, in each subclass we create additional constructors. Each of them accepts a reference to the superclass object as a parameter (plus possibly additional business-specific information specific to the class). Information common to all objects located at a given hierarchy level is copied from the inside of the received object into the newly created one. Thus for our example, by changing the "old" Student class in the "new" class Employee, we will copy name and birth date. Other information specific to Student (index number, scholarship) is lost. If they are needed, then they can be preserved, but then it will not be "pure" dynamic inheritance.

At first glance this solution looks very good. It is quite easy to implement, and "works". Unfortunately not quite. The problem may be updating relevant references pointing to an "old" object (ie, links) to point to the new one. "Appropriate" references mean those that are common to the "old" and "new" class. Other references (those specific for the "old" class) "disappear" - as well as attribute values.

If you use the ObjectPlusPlus class, the solution to this problem is much easier. This is because we have information about objects that are pointing at us - because all the links in ObjectPlusPlus are bi-directional. You also need to remember to take care of the extent, that is removing "old" objects from it and adding the "new" one.

Sample code implementing part of this functionality is shown below. As we can see, the Employee class has a constructor (24) that retrieves an instance of the Person class (actually its subclass, because Person is an abstract class) and Employee-specific parameters. And where is the copying attributes code we wrote about? Thanks to the call of the super class (26) we do not have to do this manually. Then the employee-specific information is stored (29).

Java
public abstract class Person {
	protected String firstName;
	protected String lastName;
	protected LocalDate birthDate;

	public Person(String firstName, String lastName, LocalDate birthDate) {

		this.firstName = firstName;
		this.lastName = lastName;
		this.birthDate = birthDate;
	}

	public abstract float getIncome();

	@Override
	public String toString() {
		return this.getClass().getSimpleName() + ": " + firstName + " " + lastName;
	}
}

public class Employee extends Person {
	private boolean medicalTest;
	private float salary;

	public Employee(String firstName, String lastName, LocalDate birthDate, boolean medicalTest, float salary) {
		super(firstName, lastName, birthDate);

		this.medicalTest = medicalTest;
		this.salary = salary;
	}

	public Employee(Person prevPerson, boolean medicalTest, float salary) {
		// Copy the old data
		super(prevPerson.firstName, prevPerson.lastName, prevPerson.birthDate);

		// Remember the new one
		this.medicalTest = medicalTest;
		this.salary = salary;
	}
}

And how can it be used? The example and effect are shown below. First we create the object describing the student (2), then convert it into the employee (6) and then into the pensioner (10).

Java
// Create a student
Person p1  = new mt.mas.inheritance_dynamic.copying.Student("John", "Smith", LocalDate.of(1994, 9, 14), 1212, 2000.0f);
System.out.println(p1);

// Create an Employee based on the Student
p1 = new mt.mas.inheritance_dynamic.copying.Employee(p1, true, 4000.0f);
System.out.println(p1);

// Create a Pensioner based on the Employee
p1 = new mt.mas.inheritance_dynamic.copying.Pensioner(p1, 3000.0f);
System.out.println(p1);
Plain Text
Student: John Smith
Employee: John Smith
Pensioner: John Smith
Knowledge Check
Select all that apply

Which statements are true?

What about updating the links pointing at the "old" object? This should not be difficult, because if you use the ObjectPlusPlus class, most of the required information is already in your system. In addition to copying the links you need to take care of a few things:

  • from the "old" object, we should copy only data belonging to the "superclass" (eg A) - the figure below. We do not copy associations belonging to its association (eg B, C).
  • To correctly update bindings showing the "new" object, we need information about the relationships of roles. Thus which roleName corresponds reverseRoleName. Theoretically, we can replace all instances (in all roles) targetObject showing an "old" object, but some of these updates are unnecessary because some of the links are "abandoned". In other words, when there are several associations (belonging to different associations / roles) between the same objects, we are unable to determine which items form pairs.
Dynamic inheritance
Auxiliary assignment

Develop an enhancement for the ObjectPlusPlus class that implements dynamic inheritance. It may be based on the above idea or some other improvements.

The provided solution (along with a brief description, source code and example of utilization) can receive up to 5% of the points planned for mini-projects.

Three universal ways of implementing inheritances

In popular programming languages ​​there is only the simplest type of inheritance. In fact, all other types of inheritance can be implemented by one or more of the following techniques:

  • Replace the hierarchy with a single class. The undoubted advantage of this solution is the great ease of implementation. Sometimes it may be apparent, for example, when you need to override the methods (and polymorphic call) using multiple conditional statements (eg case or ifs). Another disadvantage is the underutilization of resources.
  • Use of aggregation / composition. We use the resources in an optimal way. Depending on the implementation details, especially when we do not use pre-built libraries (eg ObjectPlusPlus), we can expect a fairly laborious implementation (including "wrapping" methods), although it is usually not too difficult.
  • Interfaces. The disadvantage is that it is quite laborious even when we apply some of the design patterns. The advantage is the wide possibilities and the fact that it is a very common technique (and therefore quite familiar to most programmers).

Summary

Remember that all the presented approaches bypass inheritance and they are not equivalent constructs. Therefore, wherever possible it is necessary to use inheritance instead of its substitutes.

Unlike associations, where we just have to decide whether we use identifiers or references, there is no perfect solution in the case of inheritance. Each case should be treated individually.

Source codes

Download icon

Download all sources for MAS lectures.