bài viết ngẫu nhiên
The Open/Closed Principle is defined as follows:
Software entities should be open for extension, but closed for modification.
It may sound a bit “philosophical,” but in practice, it’s very down to earth.
Open (for extension): easy to add new features
Closed (for modification): no need to directly change existing code, reducing the risk of domino bugs
In other words:
The code you write today should be mentally prepared for new features tomorrow—without breaking what already works.
Imagine you have a power outlet.
Tomorrow, if you want to plug in a laptop, you just plug it in.
If you need a 3-prong plug, you buy an adapter.
You don’t have to break the wall and rewire everything every time you buy a new device.
That’s exactly the spirit of OCP.
Suppose you’re building a system to calculate areas of geometric shapes.
class AreaCalculator {
calculate(shape: any) {
if (shape.type === 'rectangle') {
return shape.width * shape.height;
}
if (shape.type === 'circle') {
return Math.PI * shape.radius * shape.radius;
}
}
}
const calc = new AreaCalculator();
console.log(calc.calculate({ type: 'rectangle', width: 10, height: 20 }));
console.log(calc.calculate({ type: 'circle', radius: 5 }));
If tomorrow you want to add a Triangle, you must modify the calculate method.
Modifying existing code = risk of breaking something that was already stable.
interface Shape {
getArea(): number;
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
getArea() {
return this.width * this.height;
}
}
class Circle implements Shape {
constructor(private radius: number) {}
getArea() {
return Math.PI * this.radius * this.radius;
}
}
class AreaCalculator {
calculate(shape: Shape) {
return shape.getArea();
}
}
// Usage
const calc = new AreaCalculator();
console.log(calc.calculate(new Rectangle(10, 20)));
console.log(calc.calculate(new Circle(5)));
Now, if you want to add Triangle, you simply create a new class that implements Shape.
No existing code needs to be touched.
You can clearly see OCP in Dependency Injection.
Today you use MySQL
Tomorrow you switch to Postgres
You only need a new implementation—no changes to UserService.
interface Database {
save(data: any): void;
}
@Injectable()
class MySQLDatabase implements Database {
save(data: any) {
console.log('Save to MySQL', data);
}
}
@Injectable()
class UserService {
constructor(private readonly db: Database) {}
addUser(user: string) {
this.db.save(user);
}
}
The beauty here:
You extend behavior by adding new implementations, not by modifying existing services.
OCP can also be applied to component rendering.
For example, a Button that supports multiple styles.
type ButtonType = 'primary' | 'secondary';
const Button: React.FC<{ type: ButtonType }> = ({ type, children }) => {
if (type === 'primary') return <button className="bg-blue-500">{children}</button>;
if (type === 'secondary') return <button className="bg-gray-500">{children}</button>;
return null;
};
Problem:
To add a danger button, you must modify the component directly.
interface ButtonProps {
render: (children: React.ReactNode) => JSX.Element;
children: React.ReactNode;
}
const Button = ({ render, children }: ButtonProps) => render(children);
// Usage
<Button render={(c) => <button className="bg-blue-500">{c}</button>}>Save</Button>
<Button render={(c) => <button className="bg-red-500">{c}</button>}>Delete</Button>
Now you can extend endlessly without touching the Button component.
When the system is likely to change frequently
(e.g. new product types, payment methods, user roles)
When you want to reduce bug risks from modifying existing code
When working in a team—others can add features without breaking your code
Don’t overuse OCP.
If you try to “prepare for every possible future,” your code can easily turn into over-engineering.
If extension is very likely → apply OCP
If you’re not sure → keep it simple first, refactor later
The Open/Closed Principle doesn’t force you to predict the future.
It simply helps your code adapt gracefully to change.
Remember:
Adding should be easy. Modifying should be rare.
And next time you find yourself digging through old code just to add a tiny feature, ask yourself:
“Did I apply OCP correctly?”