Composition vs Inheritance  in Object-Oriented Programming - JavaScript example

Composition vs Inheritance in Object-Oriented Programming - JavaScript example

Composition and Inheritance

There are two main ways to reuse code in object-oriented programming: composition and inheritance. Both have their advantages and disadvantages, and the choice of which to use in a given situation is a design decision.

What is Composition?

Composition is the act of combining objects to create a new object. The new object contains references to the other objects and can delegate behavior to them. This is often seen as a has-a relationship. For example, a Person has an Address, so we can say that the person is composed of an address.

Let's take a look at how we could express it in code:

class Address {
  constructor(street, city, state) {
    this.street = street;
    this.city = city;
    this.state = state;
  }
}

class Person {
  constructor(name, address) {
    this.name = name;
    this.address = address;
  }
}

const address = new Address('123 Main St', 'New York', 'NY');
const person = new Person('John Smith', address);

console.log(person.address.street); // 123 Main St

In this example, Person is composed of its own properties and Address that is being passed to the constructor.

Pros and cons of the Composition

The advantage of composition is that it is very flexible. We can change the behavior of our Person by changing the Address object it uses (e.g add postcode to printed value). We can also create different types of Addresses, and our Person class doesn’t need to know anything about them. This also makes our code more reusable as we can use individual components in different contexts.

There are also some disadvantages to using composition in programming. First, it can require more effort to design and implement a compositional system than a monolithic one. It can also lead to code that is more difficult to debug and optimize, as the dependencies between components can be complex.

What is Inheritance?

Inheritance is the act of creating a new class that is a specialized version of an existing class. The new class inherits the behavior of the existing class, and can also add new behavior of its own. This is often seen as an is-a relationship. For example, a Dog is a type of Animal.

Code example for inheritance:

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}

const dog = new Dog('Buddy', 'Golden Retriever');

console.log(dog.name); // Buddy
console.log(dog.breed); // Golden Retriever

In the example above, the Dog class is derived from the Animal class. The Dog class inherits the name property from the Animal class.

Pros and cons of the Inheritance

The advantage of inheritance is that it can be used to model real-world relationships. Our Dog is a type of Animal, so it makes sense that it would inherit from the Animal class. It allows you to reuse code from a parent class in a child class. This can save you a lot of time and effort when you are developing a new class. Inheritance can also make your code more flexible and extensible. If you need to add a new method to a child class, you can simply override the parent class's method.

However, inheritance can also make your code more complex and difficult to understand. If a child class inherits from multiple parent classes, it can be hard to keep track of which methods are inherited from which class. This can make your code more difficult to maintain and debug. Additionally, inheritance can sometimes lead to unexpected behavior. If a child class inherits a method from a parent class that has been overridden in the child class, the child class's method will be called instead of the parent class's method.

Which one to use?

As usual in programming - it depends.

In general, you should use inheritance when you have a clear, linear relationship between the child and parent classes. For example, if you have a class hierarchy where each class adds a new layer of functionality on top of the previous one, inheritance makes sense.

Composition, on the other hand, is more flexible. It allows you to build objects from other objects, without having to follow a strict linear hierarchy. This can be useful when you want to reuse existing code or when the relationship between the child and parent classes is more complex.

There are pros and cons to both inheritance and composition, and the best way to choose between them is to understand when each one is appropriate.