Open Closed Principle (OCP)

Second, from SOLID principles. The general explanation is that “code should be open for extension, but closed for modification”. It is not obvious to me what this means in practice. Perhaps it is better explained by the consequence of not following this rule. Changing the declaration of a method may cause it to malfunction somewhere it is used. The main point is that the changes have to be backwards compatible. Of course, it's best to write code that works perfectly from the beginning and you don't have to change it, but we don't live in a perfect world.
I will try to present it with some examples.
Examples
Open/Closed API
Let’s see it clearer with the example.
This will be an example of the open/closed principle not on a single class but on the entire API. It's a large SaaS, it is an accounting system written in PHP, Symfony Framework. Your API is used by several hundred customers who use it to issue invoices. Your API has a method to retrieve invoices as PDFs. Let's say it is an endpoint like “GET /invoice/{id}/print”. Everything is fine, but one day customers demand the option to download CSV (everyone from business loves tables).
So you implement this capability quickly and change the endpoint from:
GET /invoice/{id}/print
to
GET /invoice/{id}/{format}
where the format can be PDF or CSV.
Now only hundreds of programmers using your API have to change how they download the report in PDF. Well, no, it shouldn't be done that way. How to do it correctly? Unfortunately, it is sometimes necessary to see potential problems and anticipate possible future changes. From the beginning, your endpoint did not follow the open/closed principle because it was not closed for modification. Your endpoint should assume that the need for other formats may arise someday.
Open/Closed animals
Another example is a more classic one. Let's say we have several different animal classes:
class Dog
{
public function bark(): string
{
return 'woof woof';
}
}
class Duck
{
public function quack(): string
{
return 'quack quack';
}
}
class Fox
{
public function whatDoesTheFoxSay(): string
{
return 'ring-ding-ding-ding-dingeringeding!, wa-pa-pa-pa-pa-pa-pow!';
}
}
And a class that allows animals to communicate:
class Communication
{
public function communicate($animal): string
{
switch (true) {
case $animal instanceof Dog:
return $animal->bark();
case $animal instanceof Duck:
return $animal->quack();
case $animal instanceof Fox:
return $animal->whatDoesTheFoxSay();
default:
throw new \InvalidArgumentException('Unknown animal');
}
}
}
Is the Communication class open for extension and closed for modification? To answer this question, we can ask it differently. Are we able to add a new animal class without changing the existing code? No. Adding a new animal class would necessitate the modification of the switch in the communicate() function. So what should our code look like to comply with our principle? Let's try to improve our classes a bit.
We can start by adding an interface Communicative and using it in our classes.
We create a interface to use
interface Communicative
{
public function speak(): string;
}
class Dog implements Communicative
{
public function speak(): string
{
return 'woof woof';
}
}
class Duck implements Communicative
{
public function speak(): string
{
return 'quack quack';
}
}
class Fox implements Communicative
{
public function speak(): string
{
return 'ring-ding-ding-ding-dingeringeding!, Wa-pa-pa-pa-pa-pa-pow!';
}
}
After that, we can change the Communication class so that it complies with the open/close principle.
class Communication
{
public function communicate(Communicative $animal): string
{
return $animal->speak();
}
}
How to code according to the opened/closed principle?
In code, it is worth using interfaces and sticking to them. However, if you need to change something, consider the decorator pattern.
A class or method should be small enough and have one specific task so that no future event can necessitate modification (single responsibility principle). But you also need to consider whether there may be a need for changes in the future, such as a new response format or an additional parameter, your code should be closed for modification.
Conclusion
Abstraction is a key. In many ways this principle is at the heart of object oriented design.
This is what Robert Martin wrote about OCP:
