Inheritance is one of the most if not the most important OOP concept. If implemented properly it should keep us safe from the unnecessary code duplication.
The great feature of inheritance is that it brings in a possibility to create a hierarchical structures. Inheritance describe “is-a” relationship and it is used whenever there is a need to have a more specialized class that has all the properties of base class and more. For example let’s consider a situation from OOP101 in AX 2012 part 1 where we have a Car. This time let’s look at the Car as a part of the bigger group called Vehicles, we can model this relationship with an inheritance pattern like below.
in Vehicle class is denoted using italics this means that it is abstract which implies that the Vehicle class has to be an abstract class, also we can see that the method printInfo has been overridden in derived class. Vehicle is an abstract because we should not be able to instantiate it on its own, ultimately what is a vehicle without a context like a particular car … exactly … it is an abstract concept. One of the most important advantages of inheritance over interfaces is that we can provide a default functionality. But what if we know that all the derived classes should have a functionality (like operate) but don’t have enough details in the context of the base (abstract) class? In such a scenario the solution is to create an abstract method which is basically a contract that obliges us to provide functionality later in the derived classes. In X++ we can provide only one base class; it does not support multiple inheritance like for example C++, but that is actually a good thing because it protects us developers from implementing ambiguous scenarios like the diamond problem. In such a scenarios where we wish that we could use multiple inheritance we can use interfaces instead.
X++ implementation of Vehicle/Car scenario could look as follows:
abstract class Vehicle
{
str owner;
int age;
void printInfo()
{
/* default functionality */
}
abstract void operate()
{
}
}
class Car extends Vehicle
{
str model;
void printInfo()
{
/* overriding default functionality */
}
abstract void operate()
{
/* implementation */
}
}
It is also worth noting that when we are overriding functionality in derived classes we can call the default functionality from the base class by using super keyword. In part three of this series I will go through the major benefit that implementing inheritance and interfaces gives, namely polymorphism.