Snow the bunny.

What is the Open-Closed Principle?

Let’s look at the Open-Closed Principle in object-oriented programming and how can it help you develop better applications and software.

Making cats, dogs and rabbits

Yesterday I wrote about the Single Responsibility Principle and how to use it to code better dogs.

Imagine I’ve created an Animal class with a few methods for most animals do, such as sleep and making sounds.

class Animal {
  constructor(type, color) {
    this.type = type;
    this.color = color;
  }

  sleep() {
    console.log(`The ${this.color} ${this.type} is sleepy.`);
    setTimeout(() => {
      console.log(`The ${this.color} ${this.type} took a nap.`);
    }, 3000);
  }

  makeSound() {
    console.log(`The ${this.color} ${this.type} made a sound.`);
  }
}

Now that I’ve created this Animal class, I can create some child classes that will inherit its methods and properties.

class Cat extends Animal {
  constructor(color) {
    super("cat", color);
  }
}

class Dog extends Animal {
  constructor(color) {
    super("dog", color);
  }
}

class Rabbit extends Animal {
  constructor(color) {
    super("rabbit", color);
  }
}

Finally I can create instances of the Cat, Dog and Rabbit classes.

const cat = new Cat("orange");
const dog = new Dog("black");
const rabbit = new Rabbit("white");

So far so good. If I want either the cat, dog, or rabbit to sleep or make a sound, I can just call either the makeSound or sleep methods.

Animals don’t make the same sounds

Currently, the makeSound method only allows me to “make a sound.” But in real life, most cats meow, most dogs bark, and most rabbits don’t make a sound at all. So this method isn’t really doing what I want it to do.

I could fix this by adding a conditional statement to the makeSound method like this:

makeSound() {
  if (this.type === "cat") {
    console.log(`The ${this.color} ${this.type} said meow.`);
  } else if (this.type === "dog") {
    console.log(`The ${this.color} ${this.type} said woof.`);
  }
}

Now if the animal is a “cat” type, it will meow, but if it’s a “dog” type, it will woof.

Notice I didn’t put a condition for a “rabbit” type. Also, what if I want to create more animal types like kangaroos, orangutans, and horses? Am I going to have to make a condition for each type?

Open-Closed Principle

According to Wikipedia’s definition of the Open-Closed Principle, software entities such as classes and functions “should be open for extension, but closed for modification”.

What does that mean? It’s a bit tricky to explain in abstract terms, but looking at the classes we currently have, I think we’re trying to modify the parent Animal class with the makeSound method, when we should rather just extend the Animal class with specific methods for making a sound.

Let’s refactor our current classes to fix this.

Refactoring to support the Open-Closed Principle

So here’s a new Animal class:

class Animal {
  constructor(type, color) {
    this.type = type;
    this.color = color;
  }

  sleep() {
    console.log(`The ${this.color} ${this.type} is sleepy.`);
    setTimeout(() => {
      console.log(`The ${this.color} ${this.type} took a nap.`);
    }, 3000);
  }

  sniff() {
    console.log(`The ${this.color} ${this.type} sniffed.`);
  }
}

There now is no makeSound method. But I have added a sniff method, since most animals sniff the same.

And the updated Cat, Dog and Rabbit classes:

class Cat extends Animal {
  constructor(color) {
    super("cat", color);
  }

  meow() {
    console.log(`The ${this.color} cat said meow.`);
  }
}

class Dog extends Animal {
  constructor(color) {
    super("dog", color);
  }

  bark() {
    console.log(`The ${this.color} ${this.type} said woof.`);
  }
}

class Rabbit extends Animal {
  constructor(color) {
    super("rabbit", color);
  }
}

const cat = new Cat("orange");
const dog = new Dog("black");
const rabbit = new Rabbit("white");

cat.meow();
dog.bark();
rabbit.sniff();
cat.sleep();
dog.sleep();
rabbit.sleep();

So now, instead of attempting to call the makeSound method, each animal has a method specific to the sound it makes. Cats meow. Dogs bark. And rabbits don’t make any sounds, except maybe squeak if you accidentally step of them.