You should be able to extend the behavior of a system without having to modify that system.
Robert C. Martin
Welcome back to the series on SOLID. By now, I’ll assume you’ve read my first post on the Single Responsibility Principle. That is, each class should only have one responsibility (or a class should have only one reason to change). Now, I believe regarding OOP (Object Orientated Programming), the Open/Closed Principle refers to the fact that if you want to extend a class, you shouldn’t have to modify functionality as well. That means, say you’re using inheritance; you shouldn’t have to modify a base method.
Breaking the principle
Let’s dive into this SOLID principle. We’ll start with a Piano class and a Play method. This method will describe the way we will make music. Sounds easy right? Well, let’s look below.
public class Piano { public string Name => "Piano"; public void Push(string note) { System.Console.WriteLine($"Pressing {note}"); } public void Play() { System.Console.WriteLine("Pushing a key"); } }
Now your boss, she decides that you should also be able to play a Guitar also. What do you do? You create a base Instrument class that has the play implementation for both a piano and guitar.
public class Piano : Instrument { public string Name => "Piano"; public void Push(string note) { System.Console.WriteLine($"Pressing {note}"); } } public class Guitar : Instrument { public string Name => "Guitar"; public void Strum(string note) { System.Console.WriteLine($"Strumming {note}"); } } public class Instrument { public void Play() { if (this.GetType() == typeof(Piano)) { System.Console.WriteLine("Pushing a key"); } if (this.GetType() == typeof(Guitar)) { System.Console.WriteLine("Strumming a string"); } } }
Again, your boss comes to you. This time she wants to add a Trumpet. So you have to modify the base class again. You can see where I’m going with this, can’t you? Every time you want to extend the functionality of your Instrument class, you have to modify it. Therefore we can say that it’s not open for extension and closed for modification.
Fix using the principle
So, how do we fix this? Well, the easiest way to fix this is by using abstract methods. That is, methods on the base class that are implemented on each subclass. Now, we’ll see that we no longer have to update the base class every time we want to extend it. We can play whichever Instrument we want, and they’ll all play their notes.
public class Piano : Instrument { public override string Name => "Piano"; public void Push(string note) { System.Console.WriteLine($"Pressing {note}"); } public override void Play() { System.Console.WriteLine("Pushing a key"); } } public class Guitar : Instrument { public override string Name => "Guitar"; public void Strum(string note) { System.Console.WriteLine($"Strumming {note}"); } public override void Play() { System.Console.WriteLine("Strumming a string"); } } public abstract class Instrument { public abstract string Name { get; } public abstract void Play(); }
O for Open/Closed Principle
Hopefully, this has explained a little bit about the open/closed principle. I mean, there are probably hundreds (if not thousands) more examples online if you’re still confused. I think a shape is the best demonstration of this principle, but I like instruments better. You should be writing base classes that have common code for all subclasses. For example, maybe you want to log every time an interface method is called. You can implement the logging in the base class and then call an abstract internal method on the subclasses. There should be only one reason to change – and that’s where your base class method is wrong. Not that you’re adding a new subclass.
Leave a Reply