DEV Community

Duncan McArdle
Duncan McArdle

Posted on

SOLID principle #2: Open-Closed (JavaScript)

The open-closed principle says that code should be open for extension, but closed for modification. What this means is that if we want to add additional functionality, we should be able to do so simply by extending the original functionality, without the need to modify it.

To explain this, let’s look at an example. Below we have a Vehicle class. When a Vehicle instance is created, we pass in the fuel capacity and fuel efficiency. To get our range, we simply multiply our capacity by our efficiency.

class Vehicle {
constructor(fuelCapacity, fuelEfficiency) {
this.fuelCapacity = fuelCapacity;
this.fuelEfficiency = fuelEfficiency;
}
getRange() {
return this.fuelCapacity * this.fuelEfficiency;
}
}
const standardVehicle = new Vehicle(10, 15);
console.log(standardVehicle.getRange()); // Outputs '150'

But let’s say we add a new type of vehicle; a hybrid vehicle. This vehicle doesn’t just have standard fuel-based range, it also has an electric range which it can use as well. To find out the range now, we need to modify our getRange() method to check if the vehicle is hybrid, and add its electric range if so:

class Vehicle {
constructor(fuelCapacity, fuelEfficiency) {
this.fuelCapacity = fuelCapacity;
this.fuelEfficiency = fuelEfficiency;
}
getRange() {
let range = this.fuelCapacity * this.fuelEfficiency;
if (this instanceof HybridVehicle) {
range += this.electricRange;
}
return range;
}
}
class HybridVehicle extends Vehicle {
constructor(fuelCapacity, fuelEfficiency, electricRange) {
super(fuelCapacity, fuelEfficiency);
this.electricRange = electricRange;
}
}
const standardVehicle = new Vehicle(10, 15);
const hybridVehicle = new HybridVehicle(10, 15, 50);
console.log(standardVehicle.getRange()); // Outputs '150'
console.log(hybridVehicle.getRange()); // Outputs '200'

This violates the open-closed principle, because whilst adding our new HybridVehicle class we have had to go back and modify the code of our Vehicle class in order to make it work. Going forward, every time we add a new type of vehicle that might have different parameters for its range, we’ll have to continually modify that existing getRange function.

Instead what we could do, is to override the getRange method in the HybridVehicle class, giving the correct output for both Vehicle types, without every modifying the original code:

class Vehicle {
constructor(fuelCapacity, fuelEfficiency) {
this.fuelCapacity = fuelCapacity;
this.fuelEfficiency = fuelEfficiency;
}
getRange() {
return this.fuelCapacity * this.fuelEfficiency;
}
}
class HybridVehicle extends Vehicle {
constructor(fuelCapacity, fuelEfficiency, electricRange) {
super(fuelCapacity, fuelEfficiency);
this.electricRange = electricRange;
}
getRange() {
return (this.fuelCapacity * this.fuelEfficiency) + this.electricRange;
}
}
const standardVehicle = new Vehicle(10, 15);
const hybridVehicle = new HybridVehicle(10, 15, 50);
console.log(standardVehicle.getRange()); // Outputs '150'
console.log(hybridVehicle.getRange()); // Outputs '200'

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay