Introduce Inheritance & Object Composition

How to reuse code with the Open-Close-Principle

Itchimonji
CP Massive Programming
6 min readSep 8, 2020

--

How to reuse code with the Open-Close-Principle

Nowadays, a decoupled and encapsulated software system is more relevant for our applications. Reusability, maintenance, care, and sustainability play a significant role in our software development projects. People, who try to master these challenges, read Robert C. Martins Clean Code, Clean Coder, or Clean Architecture. Some of them follow his SOLID principles:

  • Single Responsibility Principle
  • Open-Closed-Principle
  • Liskov-Substitution-Principle
  • Interface-Segregation -Principle
  • Dependency-Inversion-Principle

So, why should we care about the so-called “Inheritance & Object Composition”? Because they are a big part of one of the aforementioned SOLID principles: the Open-Closed-Principle.

„Software entities … should be open for extension, but closed for modification.“

Moreover, inheritance and object composition decide whether our software application will be monolithic or decoupled. We need to understand these concepts to make our software systems cleaner.

What is Inheritance?

In short, inheritance describes an “is a”-relationship between 2 participants. For example, the earth is a planet, which is a celestial body. Or a cube is a geometric figure.

Simple inheritance

Facts about inheritance:

  • is one of the few core concepts of object-oriented programming
  • a subclass inherits fields and methods from their base class/superclass
  • so, generally speaking, the subclass is a specialized model of its generalized base class
  • properties or methods are only passed on according to their visibility (e.g., private, protected, public, …) and may be overwritten by their subclasses
  • is supported by OOP — so handling is effortless
  • definition at design time
  • implementations can be reused in the subclasses very easily

The “Gang of Four”, who wrote the book Design Patterns, describe inheritance as a white-box reuse because the internals of base classes are visible to their subclasses. And because of the strong linkage between base and subclass, the modification of the base class can affect the subclass, too. So, the base class implementation breaks up the encapsulation of all subclasses — this might be hard to change later.

This relationship is tightly coupled and entails many code risks by changing the superclass. The more a class knows about another class, the more they are coupled. Only the inheritance of abstract classes can prevent this.

class Creature {
private isNatural: boolean;
protected size: number;

public beAlive(): void {
console.log('Im alive');
}
constructor (isNatural: boolean) {
this.isNatural = isNatural;
}
}
class Human extends Creature {
protected age: number;
public setSize(value: number): void {
this.size = value;
}
constructor(age: number) {
super(true);
this.age = age;
this.beAlive();
}
}
class Worker extends Human {
constructor(size: number) {
super(38);
this.beAlive();
this.setSize(size);
}
}

What is Object Composition?

Another technique of code reuse is object composition. In short, it describes a “has a”-relationship between 2 or more participants. For example, a car has wheels, an engine, windows, doors, and much electronic stuff.

Object Composition with Dependency Inversion

Facts about object composition:

  • is fundamental to every programming language (not only OOP)
  • it is an assignment of a reference to other objects
  • it can be accessed by interfaces (Dependency Inversion)
  • we can change this assignment whenever we want — so this runtime behavior allows replacing different objects of the same type dynamically
  • definition at runtime

The “Gang of Four” describe Object Composition as black-box reuse because we can not see the internal code — only the interface.

This loosely coupled concept and the interface access allow more flexibility in code design, secure Uncle Bobs Open-Closed-Principle, and lead to encapsulation. Because our existing and working code will not be altered while we can still add new behavior — we can change components and objects without any code risks and without having to think about code dependencies.

Besides, using object composition is an excellent method to mock test data.

interface AbstractEngine {
howMuchIsIt(): void;
}
class DieselEngine implements AbstractEngine {
private cycle: string;
private compressionRatio: string;
public howMuchIsIt(): void {
console.log('High initial and maintenance costs');
}
constructor() {
this.cycle = 'Diesel';
this.compressionRatio = 'high';
}
}
class PetrolEngine implements AbstractEngine {
private cycle: string;
private sounds: string;
public howMuchIsIt(): void {
console.log('Low initial and maintenance costs');
}
constructor() {
this.cycle = 'Otto';
this.sounds = 'Brumm brumm..';
}
}
class Car {
public engine: AbstractEngine;
public drive();
}
function buildCar(engine: AbstractEngine): Car {
const result: Car = new Car();
result.engine = engine;
return result;
}
function buildCarWithPetrolEngine(): Car {
const result: Car = buildCar(new PetrolEngine());
result.howMuchIsIt(); // Low initial and maintenance costs
return result;
}
function buildCarWithDieselEngine(): Car {
const result: Car = buildCar(new DieselEngine());
result.howMuchIsIt(); // High initial and maintenance costs
return result;
}

What to prefer: Inheritance or Object Composition?

The “Gang of Four” write in Design Patterns we should prefer composition over inheritance whenever we can because of the decoupled principle.

Both have strengths and weaknesses, but most developers instinctively opt for inheritance. Maybe they learned it through their education and gained more experience than with composition. I use object composition more than inheritance.

What we prefer depends on our problem. Most UI frameworks are based on inheritance (e.g., WinForms in C#). On the other hand, most Design Patterns use object composition.

“Program to an interface, not an implementation.”

Conclusion

These days it is essential to design our software applications more decoupled and encapsulated. Object composition is a concept of reusing code more flexibly. Inheritance is better known and easier to handle. Also, we can combine both ideas. I started to use object composition more often after reading the book “Design Patterns”. I learned it is essential to build our software system in an easily maintainable way. We should try to be DRY and reuse our code.

Design Patterns are great, and it is fun to code them — object composition is a big part of it. Besides, we learn other coding techniques — for me, I now better understand Angular with its own art (Dependency inversion, observables, states, etc.). It is worth taking a look at them. Try it out!

I created a cheat sheet for every pattern with example code on GitHub. Just check it out!

Thanks for reading! Follow me on Medium, Twitter, or Instagram, or subscribe here on Medium to read more about DevOps, Agile & Development Principles, Angular, and other useful stuff. Happy Coding! :)

--

--

Itchimonji
CP Massive Programming

Freelancer | Site Reliability Engineer (DevOps) / Kubernetes (CKAD) | Full Stack Software Engineer | https://patrick-eichler.com/links