Open Closed Principle

Take advantage of OCP to write highly extendable and maintainable software. Introduce new changes with minimal impact on the existing system. OCP seems to increase complexity of code due to abstraction levels it introduces. Embrace it pragmatically, find good candidates for OCP while designing software.

OCP states, our classes should be open for extension but closed for modification. Whenever we have to implement a change or add new functionality, instead of changing the existing class we should extend the functionality of existing class. Let’s try to understand with a scenario below.

In our example for Single Responsibility Principle, we used a logger to log exception messages to a text file. In future we may have a requirement to log some error messages to database or application even log. How should we design our software to allow extension to logging functionality in future?

Let me code:

For OCP example I have modified CampaignManager class a bit. Now it expects Logger class object in constructor.

    public class CampaignManager
    {
        Logger logger;
        public CampaignManager(Logger logger)
        {
            this.logger = logger;
        }

        public void LaunchCampaign(Campaign campaign)
        {
            try
            {
                // Code to launch the campaign
            }
            catch (Exception exception)
            {
                logger.Error("Exception occured", exception);
            }
        }
    }

    internal class Logger
    {
        public void Error(string logMessage, Exception exception)
        {
            //Log error message to text file
        }
    }

Our logger logs messages to text file right now. For logging them to database we have two options here. We can introduce a new method ErrorToDatabase in Logger class or add an additional parameter to Error method indicating the target (database/text file). Let’s proceed with first option.

    internal class Logger
    {
        public void Error(string logMessage, Exception exception)
        {
            //Log error message to text file
        }
        public void ErrorToDatabase(string logMessage, Exception exception)
        {
            //Log error message to database
        }
    }

Now CampaignManager can call Error or ErrorToDatabase method depending on where it wants message to be logged. This serves the purpose but it leaves our software in a state where extension to existing functionality is a pain. In future if we want to log our messages to Application Event log, are we going to introduce another method in the same class?

Take advantage of OCP for such situations and leave existing class in almost unchanged state. How?? implement OCP using polymorphism and you are at peace

Let me code

Take some time to think over while designing modules/units of a software which you think might change in future as Logger in our example. The implementation would change totally

    internal abstract class Logger
    {
        public abstract void Error(string logMessage, Exception exception);
    }

    internal class TextFileLogger : Logger
    {
        public override void Error(string logMessage, Exception exception)
        {
            //Log error message to text file
        }
    }

We have introduced an abstraction on logger. This will allow us to create as many child classes of this abstract class as we want meaning we can always extend the functionality of logger leaving the existing one unchanged. For instance to have a database logger we simply create another class

    internal class DBLogger : Logger
    {
        public override void Error(string logMessage, Exception exception)
        {
            //Log error mesaage to database
        }
    }

Since our CampaignManager accepts Logger object, we can pass it either the object of DBLogger or TextFileLogger.

Question: What about the Application Event Logger?

Answer: Left out to your imagination. Hint: Look at how we achieved database logging 🙂

Question: Where to take decision which Logger should be used?

Answer: Not in scope of this article but we will learn about resolving dependencies in the last principle i.e. Dependency Injection.

Leave a Reply