CSC/ECE 517 Fall 2012/ch1 1w24 nr
Decomposition, message forwarding, and delegation versus inheritance in OOP
Introduction
Inheritance plays an important role in designing reusable object oriented software. In this article we will discuss some alternatives to inheritance to achieve object oriented code reusability. Design techniques like composition, message passing and delegation can be used instead of inheritance to achieve the same goals with more cohesion and less coupling. The basic difference in these approaches lies in the separation of interface and implementation. We shall look at these concepts one at a time and make the differences clear with examples.
Definitions
Inheritance
Inheritance is an object oriented programming concept for code reuse. It says that we can define a subclass on a parent class and this subclass will inherit its parent’s attributes and behavior. Consider the following example:
class A { protected int attribute1, attribute2; public void method1() { //method definition } public void method2() { //method definition } } class B extends A { public void method1() { //method overriding } public void method3() { //method definition } }
Here, class B is a subclass that inherits the attributes and method defined by its parent A. The subclass inherits the method interface as well as the implementation from its parent. The subclass may define its own attributes and methods or it may provide its own implementation for its parent’s methods. This is called method overriding.
Composition
Object composition is an alternative to class inheritance to achieve code reusability. Here, new complex functionality is achieved by assembling or composing objects [1]. Unlike inheritance, composition relies on reuse of interface rather than implementation. Consider the new object as a large box. Complex functionality is achieved in this larger box by placing smaller closed boxes inside this larger box. Every smaller box has its own independent implementation hidden from the outer box. Thus composition provides encapsulation in the true sense. This is why composition is also called the black box approach for code reuse. As an example consider a chair class. A chair is made up of a seat, backrest and four legs.
public class Seat { … } public class Backrest { … } public class Leg { … } public class Chair { Seat s = new Seat(); Backrest b = new Backrest(); Leg l = new Leg(); … }
In this example since seat, backrest and legs are components of a chair it is better to use composition instead of inheritance. The seat, backrest and legs class have their own implementation which is hidden from the chair class. These components can be used in the chair class with the help of their defined interface. The chair class can communicate with its component objects through message passing. Thus, code reusability is obtained without caring for the implementation of the component objects.
Aggregation
Another related concept is that if Aggregation. Suppose we also use a class Person in the Chair class to represent the person occupying the chair. Then this person is not initialized in the Chair class as a person has an existence irrespective of the chair, whereas the seat. Backrest and legs do not have an existence without the chair. So, the person object is initialized outside the chair class and can be used inside it with its interface.
Delegation
Delegation is a way of making composition as powerful for reuse as inheritance [2]. Delegation involved two objects: a delegator who delegates a operation to another object, the delgatee. This can be compared to inheritance where a subclass defers request to the parent class. The advantage of delegation over inheritance is that delegation allows composition behavior to occur dynamically at run-time as against Inheritance which causes the relationship to be established statically at compile-time. Although overuse or delegation may make the code hard to understand and may invite runtime inefficiencies.
Let us convince ourselves that Inheritance is bad at certain places
Below are the cases[3]:
Problem 1: Multiple Inheritance introduces lot more confusions[4]
One such is Diamond Problem. Two classes B and C inherit from the same base class A and class D inherits from B and C. If D calls a method defined in class A and implemented very differently in classes B and C.
Class A { virtual print() { cout << “This is class A”; } } class B : A { override print() { cout << “This is class B”; } } class C : A { override print() { cout << “This is class C”; } } class D : B, C { A a = new A(); a.print(); }
Inheritance is link time
Types are fixed during compile time and super classes cannot be changed dynamically during program execution. Another problem which arises because of static linking is, the changes to super class automatically propagates to the sub-class. In essence inheritance creates a strong coupling. This can be addressed with help of Composition, which allows to change behaviour at runtime.