Inheritance and its misunderstandings in the Object-oriented design
Recently I had a conversation with one of my students in the “Advanced Object-Oriented Design” course about why nowadays Functional programming paradigm is so interesting while for decades Object-oriented paradigm has been dominated paradigm. If you do some search on the web you find dozens of articles and debates about this topic, so, I don’t want to restate what has been investigated deeply in the other documents; instead, I am going to discuss one of the bases that make such skepticism about Object Oriented Paradigms. The inefficiency of the inheritance as the most significant hierarchical relationship between objects to increase reusability and protect applications against changes is one of the reasons has been used as a basis of the argument which suggests returning to a functional paradigm which has less complexity than object-oriented. I believe the main logic behind such a case is inheritance misunderstanding. Thus, in this article I try to provide some description to make this concept clearer and show why there is such a misconception;
We all heard about the role of the inheritance for creating a reusable software, but the neglected point is that which part of the software is the subject of the reuse. As inheritance has an intrinsic relationship with abstraction and polymorphism concepts, it worth to investigate this concept while there is an eye on these fundamental object-oriented phenomena.
The inheritance can be applied to reuse software in two entirely philosophical different mentalities, the first one is “Code or Implementation Reuse” and the second one is “Type or Contract Reuse.”
The “Code or Implementation Reuse” means that class B is derived from class A to have the same structure and data members as its father class “A” have, moreover there may exist some implemented methods which class B wants to reuse them to prevent needless repetition in the code. In this approach, it may be no change in the implementation of the base class behaviors; in another word, there is no overriding for neither protected nor public methods of class A in class B, which means class B has no change to the exposed behavior in class A but some new methods. The following picture shows regardless of the existence of the third class as a client which use rectangle class the inheritance is occurred between rectangle and square because of their definition in the real world.
From this point of view, an inheritance relationship between two classes exists because they are similar in the real world or business domain; especially the similarity in their structure and common implementation for their methods. Although the derived classes may have some behaviors which do not make sense for the parent class; To access this added behavior in the children classes the reference to the object should be precisely a type of the derived class. Some programmers’ conception of code reuse is a belief same as the this approach, these guys believe code reuse or software reuse means if there is a chunk of code which is applicable in another part of the same software or other software, creating a derived class from the code owner class is the best technique to reusing it. However, this perception is not the best one to get rid of code repetition. Because in the software world inheritance is not the only way to use the behaviors of a class in another part of the code; “Association” relationship is a more suitable approach in such cases. Which means “has” relationship. Using this technique if class B needs a behavior which is implemented in class A instead of inheriting from A, it can contain an instance of A as its member and call the appropriate method to reuse its implementation.
Another reason behind using inheritance as a tool for code reuse and code sharing is the resemblance between objects in the real world; for example in this mindset in an object model of an organization the manager class is a derived class of the employee! Regardless of the role of these classes to solve the subject problem of the system. In this mentality, a manager is always an employee because we have this relationship in the real world. It is Nonsense! It must not be forgotten that our software is not a miniature version of the real world objects and their relationships, Instead in the software we simplify the structures of the objects and their relationship in comparison with the real world. It would be not bad if remember the inheritance in the object-oriented design approach is not the same as the inheritance concept in the nature between living creatures and their children. So it’s not reasonable to inherit a Student Class and a Teacher class from a base Person class because both of them are a type of person or Inheriting a Circle and a Rectangle class from a base shape class regardless of their role in our solution to the problem.
In contrast, the second approach to inheritance is “Type or contract Reuse,” which defines inheritance based on the expectation of a client class from other classes to provide specific services without any consideration about their implementation. In this approach, there is a client class as “C” which wants to use a service as a step of its responsibility regardless of how it may be implemented. The only essential point is that the class “C” has its standard for the signature of the service that expected to do the computation. So the client defines a contract as an interface or base class as “A” which contains the appropriate signature for the target service, now, other classes can inherit from this class and have their implementation to perform the client’s requirements. In another word, first of all, an abstraction emerges to protect a client class from the variety in the implementation of behavior, then this new abstraction will be the base class for concrete classes those have different implementation for the forced contract by their parent. From GRASP (General Responsibility Assignment Software Principles) point of view we have found that the client class is an important business container which requires to be protected against changes in the various implementation for non-critical behaviors used as a step in flow of an important process, so using Indirection and Polymorphism principles we have made an airbag to protect it. The logical result of applying indirection combined with polymorphism is an inheritance relationship between contract container class and its concrete implementations;
Using inheritance as “Type or Contract Reuse” has a different purpose than “Code or Implementation Reuse” approach, type reuse is the result of applying abstraction and polymorphism principles. Based on “Abstraction principle” an object not only make a simple representation of the real world or business domain concepts’ behaviors, but also it defines its expectation from other objects which have to provide required services that are needed to complete its principal responsibility. These services are subjects of the change because their implementation varies over time and situation. From this point of view, the target of the reuse is not service provider classes or server classes instead the goal of reuse is the client object which has defined the contracts that should be obeyed to by its servers. This perspective shows the client objects are the most important objects in the software, which we want to reuse them regardless of how the details of external services they use to establish the primary responsibility. In this approach, the inheritance relationship is a technique to provide a different implementation for required service for the client.
As a result, comparing these sights to the inheritance reveal that when we talk about reuse using inheritance, we should say “what is the topic of the reuse, the behavior in the core classes of the system or reusing the structure and implantation of the noncritical services?”. As in the Object-oriented, the behavior is matter, not the structure, reusing the business-centric behavior are the first class citizen for reuse. To recap, Inheritance is a child of abstraction principle and abstraction is not a natural characteristic of objects, Abstraction is meaningful from a client point of view that tries to get rid of implementations’ details of those steps and tasks which are subject of change in the core process. Based on this definition not only object-oriented is the best approach to reuse most valuable part of software but also it helps to upgrade and renew noncritical, technology related part of a process without any significant change in the heart of the system. As a bottom line protecting most important and competitive advantage making classes against noncritical changes in a system is the magic of the object-oriented paradigm.