JavaScript Design Patterns — Composition, Inheritance, and Configuration

John Au-Yeung - Jan 30 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Design patterns are the basis of any good software. JavaScript programs are no exception.

In this article, we’ll look at the difference between composition and inheritance, and look at why we should make our program have some flexibility.

Composition Versus Inheritance

Composition is when our object contains other objects.

Inheritance is when we receive the parent object’s members in the current object.

They both have their place.

Composition is a has-a relationship while inheritance is a has-a relationship.

It’s very important to make distinctions between the 2 since they do different things.

In JavaScript, there’re various ways that 2 things are composed.

For instance, we can have functions that are nested in other functions.

We can write:

const foo = fn => {
  //...
  fn();
  //...
}
Enter fullscreen mode Exit fullscreen mode

We have a foo function that calls fn .

Also, we can have nested objects. For example, we can write:

const obj = {
  foo: 1,
  bar: {
    baz: 2,
  }
}
Enter fullscreen mode Exit fullscreen mode

We have an object as the value of bar .

We can also have functions as values of object properties.

Composition is used for holding functionality that’s needed by something.

Inheritance, on the other hand, is an is-a relationship.

This means a child object is also a parent object. It’s just that a child object may have additional things.

For instance, if we have:

class Animal {
  //...
}

class Cat extends Animal {
  //...
}
Enter fullscreen mode Exit fullscreen mode

Then Cat is a subclass of Animal . Cat is an animal.

Cat shares all the members of Animal .

We can any methods ofAnimal and access any instance variables of Animal from a Cat instance.

For example, we can write:

class Animal {
  speak() {
    //...
  }
}
class Cat extends Animal {
  //...
}
Enter fullscreen mode Exit fullscreen mode

The extends keyword indicates that Cat inherits the members of Animal .

Then if we create a Cat instance, we can write:

const cat = new Cat();
Enter fullscreen mode Exit fullscreen mode

Then we can call speak by calling:

cat.speak();
Enter fullscreen mode Exit fullscreen mode

Likewise, we can create an is-a relationship between 2 objects with the Object.create method:

const animal = {
  speak() {
    //...
  }
}

const cat = Object.create(animal);
Enter fullscreen mode Exit fullscreen mode

Object.create takes a prototype object which will be the prototype of the child object.

So animal is the parent of cat in the example above.

Like with class instances, we can call speak by writing cat.speak() .

In both the class and object examples, the prototype of the object resides in the __proto__ property of the object.

Inheritance is good for creating multiple classes or objects that share things from the parent class or object respectively.

The general principle is that if we have volatile code, then we should use more has-a relationships rather than an is-a relationship since we assume that shared code between a parent and different children may change.

If we change shared code between parents and children, then we break all the children as well.

Creating Algorithms

Once we decided on the patterns to use, then we’ve to devise our algorithm for our program.

This should be easy once we created some basic designs. Implementation is all that’s needed in most cases.

We can use existing libraries to make our lives easier, which is what we should do in most cases.

If we have an is-a relationship between classes or objects, then we need to think about which pieces of code are shared and which ones are unique to classes or objects.

If we use a has-a model instead, then we can create algorithms in different places and use them as we wish to.

We should think about making our program configurable so that we don’t have to change code to have slightly different functionality.

This way, we make our lives easier since code change always has some risks.

To reduce that, we should make things that change frequently be configurable so that we don’t have to deal with them.

For instance, we can read settings from a file or database so an algorithm can be selected according to settings that are set in those places.

Conclusion

Our programs should have some flexibility so that we don’t have to change code for it to do different things.

Composition is when we have something in an object or class so we can use them as we wish.

Inheritance is when we have some shared code that’s used by other objects or classes.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .