[What does the letter “O” in S.O.L.I.D stand for?] — The Duty of the Developer As A Professional #7
Hi Re-Programmerz!
In the continuity of our series The Duty of The Developer as a Professional and as promised in our previous article The Golden Rule to choose a good Architecture, we will see in details the five SOLID principles, this time with the second letter: O for Open-Closed Principle (OCP).
Definition
This programming principle is first introduced by Bertrand Meyer in 1988, states that:
“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”
It is later reformulated by Robert C. Martin as follows:
“You should be able to extend the behavior of a system without having to modify that system.”
Different approaches
What OCP is about, is that it should not be a huge effort to change the behavior of a module (open for extension), and this change should not cause any source code change (closed for modification). A module, once compiled, should not be modified and be recompiled. Instead, it should be leaved as-is, and be extended when there is a need for behavior changes.
To achieve this goal, Bertrand Meyer proposed using Inheritance
“A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.”
Robert C. Martin, in his talks and books, suggests using Polymorphism (abstract classes or interfaces) while conforming to two other principles: Single Responsability Principle and Dependency Inversion Principle.
How do I know if OCP is not respected in my code?
Now, let us understand, at first, with a theoretical example, then we will bring some code in action!
In a role-playing PC game, movements and actions are triggered by the keyboard.
- To attract more players, the game company wants these to be handled also by different kinds of inputs (gamepads, joysticks…). Having OCP in mind while developing the game (and other SOLID principles as well), each time there is a new input source coming to life, the development team simply add an implementation for this new one and add this to the input options list.
- Later, for whatever the reason is, the game company decides that the Y gamepad will no longer be supported. This should require little effort from the team: delete Y gamepad’s implementation, remove Y gamepad from the input options list, the job is done. The flexibility of code makes removal as “easy” as addition.
In both cases, all other main game modules should stay unchanged, which save the team from recompiling millions of lines of source code.
… let us do some code !
The above code snippet contains an InputController
class implementing a pause
function that took one parameter : input
typed as a GameControlInput
.
The implementation of the pause
function is some switch
statement on the type of the input
parameter in order to call the correct code of thepause
action on the correct input controller.
In other words:
- If the
input
parameter is a Keyboard, theKeyboard.selectEsc
is called. - If the
input
parameter is an XboxGamePad, theXboxGamePad.selectMenu
is called. - If the
input
parameter is a PlayStationGamePad, thePlayStationGamePad.selectOption
is called.
It’s not perfect, but it does the job, let us continue…
A new requirement appears…!
The Y game pad appears on the market and the company wants to stay up-to-date by supporting this new one in their game.
What happens above?
We create a new class named YGamePad
with the same type as the other inputs.
Now we have to modify the pause
function in order to use this new class…
Wait…What?!
Clearly here, we are violating the OCP. Our InputController
class should be open to extension, not to modification!
How can we do that though? OCP in action !
Let us do some little changes…
- The
GameControlInput
is no longer a class, but an Interface. - The
pause
function inside theInputController
class has a parameter whose type conforms toGameControlInput
Interface. - The
pause
function implementation is done by a single call of the only method known by theinput
parameter :pause
. - The
Keyboard
,XboxGamePad
andPlayStationGamePad
classes, now conform to GameControlInput, have to provide their own and correct implementations of thepause
method ! TheInputController
has nothing to do with that.
Now that the OCP is respected, let us manage the new requirement!
We no longer need to modify the pause
function!
What we should do is simple: create a YGamePad
class that conforms to the GameControlInput
Interface and implement the pause
function there!
When OCP is respected and an extension is planned on your system, you only have to add new code to fulfill the task while the old one remains untouched. A code that is written once, well designed, and does not have to change could hardly become a bad code through time.
Moreover, in a customer’s point of view, when he asks to add a new feature, he actually thinks that we are adding, not modifying the old ones!
Conclusion
OCP is awesome, but is not ultimate.
The OCP-compliance design does not aim to make a system immune to all kinds of changes but only those related to the behavior extension of a class. Applying OCP while designing the code helps reduce the impacts and minimize the development team’s effort but large-scaled modifications are sometimes inevitable. This is why other design principles exist to help us enhance our code furthermore.
“The Open-closed principle is the moral center of system architecture. And while moral perfection may not ever be attainable, it’s certainly worth striving for.”
— Robert C. Martin, aka Uncle Bob
It is our job as developer to know, understand and apply the other S.O.L.I.D principles in our code to improve its quality.
Next article: the third letter from S.O.L.I.D — L for Liskov Substitution Principle!
We hope that you acquired something useful today with this post! Give us claps if you liked it and do not hesitate to share it as much as you can! 🔥
This article is co-written with Abderrahim Benmakhlouf