I recently completed an online course and these are my takeaway from Composition and Inheritance.
Inheritance
With inheritance we can extend the functionality of an existing class by overriding its method in a Child class e.g
class Parent {
function sayHi() {
console.log("sayHi from Parent");
}
}
class Child {
function sayHi() {
console.log("sayHi from the Child");
}
}
Inheritance is characterised by an "is-a" relationship between two classes e.g Apple is Fruit, Car is a Vehicle etc. Inheritance is suited for use cases where there is tight coupling between the classes which is rarely the case in the real world.
Lets say your are building a house and you need to model a window in code. So you might come up with following:
class Window {
height: number;
width: number;
open: boolean;
toggleOpen() {}
area(){}
}
The properties are self explanatory. Pay attention to the area
method. It calculates the area.
Now in addition to window we also want to add a Wall
. The code for the wall might look as follow.
class Wall {
height: number;
width: number;
color: string;
area() {}
}
You see that they share some common properties so you use inheritance for code reuse. You come up with class named Rectangle
which has common properties from the both of the classes.
class Rectangle {
height: number;
width: number;
area() {}
}
class Wall extends Rectangle {
color: string;
}
class Window extends Rectangle {
open: boolean;
toggleOpen() {}
}
So according to this code Wall
is rectangle and Window
is also a rectangle, now let's suppose the we want to introduce new type of window called circle window.
Circle window can not extend from Rectangle
window because it does not make sense. Now this is a type of Window so you might be tempted to inherit from the window class but window class itself inherits from Rectangle
so although it can be extended but it will also inherit the methods from Rectangle
class which does not make sense and it as it is derived from Rectangle
.
You wanted a banana but what you got was a gorilla holding the banana -- Joe Armstrong
So an other way is be to create a new Circle class and extend from it. It might seem a nice idea initially but as the application grows we might encounter again a similar situation. I will show on later how we can resolve this via composition.
class Circle {
radius: number;
area(){}
}
class CircleWindow extends Circle {
open: boolean;
toggleOpen() {}
}
Composition
For this I would quote the following text from GoF Design Patterns Book
Object composition is an alternative to class inheritance. Here, new functionality is obtained by assembling or composing objects to get more complex functionality. [...] Object composition is defined dynamically at run-time through objects acquiring references to other objects
To recap, composition is an alternative strategy to class Inheritance for code reuse. In composition a class will have reference to another class instance as its memeber variable. Lets see this with an example. I will change the example in the last section.
interface Shape {
area(): number;
}
class Wall {
color: string;
area(){}
dimensions: Shape;
}
class Window {
open: boolean;
toggleOpen(){}
area(){}
dimensions: Shape;
}
class Rectangle implements Shape {
height;
width;
area(){}
}
class Circle implements Shape {
radius;
area(){}
}
So in this example inside Wall
and Window
we have a member variable called dimensions, which is of type Shape. The interface Shape
guarantees us that their will be always a method named area
present. So now we both Window
and Wall
can accept any object for its memember variable dimensions as long it satisfies the interface Shape
, in a way we have delegated the responsibility for calculating the area to the class Rectangle
and Circle
Inheritance vs Composition
So this was a quick recap of inheritance vs composition. This is very difficult to explain topics but with time you develop instincts for this. Now lets take a look at Abstract
class and Interface
. These are features provided by programming languages to aid in inheritance and composition.
Abstract Class
Abstract
classes are classes for which you create an instance of them. They maybe used to provide a default implementation in case the overriding classes does not provide one. In javacript you can create an abstract class by adding the keyword abstract
infront of the class.
abstract class Destroyer {
onDestroy() {
db.close();
analytics.send("Database Closed);
}
}
class MyDestroyer extends Destroyer {
}
The class MyDestroyer gets the implemntation defined in class Destroyer.
Interface
Interface
are used to define set of properties that must exist on class. Interface only contains what a class implentating that interface should have it does not dictate what it should do. Interface are loosely coupled compared to Abstract classes and are usually favoured.
In next part of the article I will show an example of inheritance and how to convert it into composition along with use of abstract
and interface
. stay tuned!🙂
P.S: if you find any mistakes please let me know in comments. Design Patterns are difficult to understand and even harder to write about.
Top comments (1)
Nice quote by Armstrong. Those who are new to OOP, can see devopedia.org/object-oriented-prog...