Code Tips #4: Let subclasses provide defaults

Inheritance is a powerful mechanism which allows us to share code and avoid duplication, but as Marvel’s comics teach us “With great power comes great responsibility”. Imagine a scenario where you have a superclass and some subclasses, the superclass provides some default values for the instances in case the parameter value is not passed.

class Vehicle{
  constructor(args = {}){
    this.speed = args.speed === undefined ? 100 : args.speed;
    this.color = args.color === undefined ? 'red' : args.color;
  }
}

The problem with this design is that if a subclass wants to provide different defaults (e.g. for the speed property) it has to do something like this:

class Car extends Vehicle {
  constructor(args = {}){
    let speed = args.speed;
    if(speed === undefined){
      speed = 150;
    }
    const newArgs = {
      speed: speed,
      color: args.color,
    }
    super(newArgs);
  }
}


const car1 = new Car();
console.log(car1.speed); // 150

The code works but is not that easy to read. Moreover, if another class is added, let’s say a SportCart that extends the Car, it will have to add its defaults in the same ugly way. A more clear way would be to create methods that provide the defaults, if a subclass whats to provide a different default value, it can override the method.

class Vehicle {
  constructor(args = {}){
    this.speed = args.speed === undefined ? this.getSpeed() : args.speed;
    this.color = args.color === undefined ? this.getColor() : args.color;
  }

  getSpeed(){
    return 100;
  }

  getColor(){
    return 'red';
  }
}

class Car extends Vehicle {
  constructor(args){
    super(args);
  }

  getSpeed(){
    return 150';
  }
}

const car1 = new Car();
console.log(car1.speed); // 150

This mechanism is an implementation of what is known in the bibliography as template method pattern.

Leave a Reply

Your email address will not be published. Required fields are marked *