SOLID Principles (3 Part Series)
1 Single Responsibility Principle
2 Open Closed Principle
3 Liskov Substitution Principle
Now it’s the time to start another journey with the second SOLID principle, the Open-Closed.
Open-Closed principle states:
“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”
What does that even mean? How could a software entity be opened and closed at the same time!
Let’s start explaining by using a real-world scenario to illustrate the problem, then moving into the solution and the technical details.
Photo viewers are used everywhere, literally. You can find them on social media platforms such as facebook or twitter. Also, you can find them on your smartphone as gallery applications. They’re considered a key feature in many apps.
If you decided to develop a photo viewer with your own, users would expect it to open photos of any type. It could be a JPEG photo. It might be a PNG. Sometimes it’s not the case that a specific type is supported by your photo viewer. Hence, you need to change the source code of your photo viewer to support that type.
How to guarantee that a new type added to your photo viewer won’t affect an already existing one? You may edit your application to support a GIF type, but it crashes the application when you open a PNG photo. How to prevent that from happening? And how to guarantee enough flexibility to add and remove other types in the future without affecting each other.
The solution is the Open-Closed principle, as you certainly would guess.
Recalling the PhotoViewer class from the Single Responsibility article with a slight change.
class PhotoViewer{
void openPhoto(){
System.out.println("Open JPEG photo!");
}
}
Enter fullscreen mode Exit fullscreen mode
Now you wrote your photo viewer class, and you’re quite sure that it has to open JPEG photos only. Later you realized that your users are continuously trying to view PNG photos. And because your app doesn’t support it, it raises an error. So, you decided to edit the source code so that it supports PNG.
class PhotoViewer{
void openPhoto(String type){
if(type == "JPEG"){
System.out.println("Open JPEG photo!");
}
else if(type == "PNG"){
System.out.println("Open PNG photo!");
}
else{
System.out.println("Photo type is not supported!");
}
}
}
Enter fullscreen mode Exit fullscreen mode
Now, what are the problems of your code after making the changes?
What if you decided to add another type? Which is 100% going to happen now or then?
At that moment, you would add another else if statement. Adding a new type after another makes open photo function too large as a function. And because all types exist at the same place, removing some lines of code from one type may crash the others.
So the code is open for extension, which means you can extend it by other features. But not closed for modification, because every time you add a type you modify the open photo function lines of code. Many changes at the same block of code for different reasons is an indication of bad design.
Okay, I know you might have thought about separating JPEG and PNG into different functions at the photo viewer class. I’m with you, let’s try it!
class PhotoViewer{
void openJpegPhoto(){
System.out.println("Open JPEG photo!");
}
void openPngPhoto(){
System.out.println("Open PNG photo!");
}
}
Enter fullscreen mode Exit fullscreen mode
Now your code is open for extension. You can add new types easily by adding new functions. But the same problem still exists. The solution is not 100% closed for modification, as the class lines of code are going to be affected by the changes. Also notice that adding another type to the photo viewer class is going to make it larger with more responsibilities. This violates the Single Responsibility Principle.
I know you’re tired, but believe me, it worth it.
The solution is about using Object-Oriented Programming features by using a generic interface to represent a generic photo type that any new type can implement.
interface Photo{
void open();
}
class PhotoJpeg implements Photo{
void open(){
System.out.println("Open JPEG photo!");
}
}
class PhotoPng implements Photo{
void open(){
System.out.println("Open PNG photo!");
}
}
class PhotoViewer(){
void openPhoto(Photo photo){
photo.open();
}
}
Enter fullscreen mode Exit fullscreen mode
You can see that adding a new type won’t change a single line of code from the source code. You only need to implement the photo interface with the new type and send the photo to the photo viewer to be opened.
class PhotoGif implements Photo{
void open(){
System.out.println("Open GIF photo!");
}
}
Enter fullscreen mode Exit fullscreen mode
The solution is 100% open for extension and closed for modification. Notice that the single responsibility principle is also applied.
Now you know that Open-Closed means that your code is open to be extended by new functionalities and closed in terms of changing the source code, but appending to it.
Wait for a new journey with the third SOLID principle, Liskov Substitution!
SOLID Principles (3 Part Series)
1 Single Responsibility Principle
2 Open Closed Principle
3 Liskov Substitution Principle
暂无评论内容