Open closed principle

The open/closed principle (OCP) states that a module should be open to extension but closed for modification. It is one of famous 5 solid principles and very important object oriented design principle.

1. Definition of open closed principle

There are two popular definitions to describe this principle –

1.1. The Meyer definition

Bertrand Mayer, in his 1988 book, Object-Oriented Software Construction (Prentice Hall), defined the open/closed principle (OCP) as follows:

Software entities should be open for extension, but closed for modification.

1.2. The Meyer definition

Robert C. Martin has defined the OCP in his book, Agile Software Development: Principles, Patterns, and Practices (Prentice Hall, 2003), as follows:

Open for extension – This means that the behavior of the module can be extended. As the requirements of the application change, we are able to extend the module with new behaviors that satisfy those changes. In other words, we are able to change what the module does.

Closed for modification – Extending the behavior of a module does not result in changes to the source or binary code of the module. The binary executable version of the module, whether in a linkable library, a DLL, or a Java .jar, remains untouched.

2. Discussion

According to definitions cited above, for code to be open for extension, developers must be able to respond to changing requirements and support new features. This must be achieved despite modules being closed to modification. Developers must support new functionality without editing the source code or compiled assembly of the existing modules.

2.1. Exceptions

Please note that there might be few cases where modification of the code is absolutely necessary and cannot be avoided.

  • One such example is existing defects in the module. In case of fixing the defects, module code changes are allowed and their respective testcases as well.

    We can use the TDD approach to address these issues in the code. You must make sure that no other tests are failing as a side effect when the bug is fixed.

  • Another permissive exception is that any change to existing code is allowed as long as it does not also require a change to any client of that code. This allows the upgrade of the module versions with new language features. For example, Spring 5 supports and uses Java 8 lambda syntax but to use it, we do not require to change our client application code.

    A module where classes are loosely coupled, a class change without forcing other classes to change and that’s why loose coupling is encouraged. Maintaining loose coupling limits the impact that the OCP has if you allow modifications to existing code that do not force further changes to clients.

2.2. How to design for open closed principle

To enable the extension of the module, we can adapt either of two (generally used) mechanisms.

2.2.1. Implementation inheritance

Implementation inheritance uses the abstract classes and methods. You can define the extension points as abstract methods.

This abstract class can be extended by few classes which have predefined implementations for most common scenarios. For client specific scenarios, developer must extend the abstract class and provide that specific implementation logic. This will help in preserving the OCP.

Template method pattern is a great fit for these usecases. In this pattern, general steps are customizable because of delegation to abstract methods. In effect, the base class delegates the individual steps of the process to subclasses.

2.2.2. Interface inheritance

In interface inheritance, the client’s dependency on a classes is replaced with the interfaces. This is infact preferred approach than abstract methods. This echoes the advice to prefer composition over inheritance and to keep inheritance hierarchies shallow, with few layers of sub-classing.

Design for inheritance or prohibit it. – Effective Java (Addison-Wesley, 2008), Joshua Bloch

3. Open closed principle example

If you want to see the open closed principle real world example, just look at the Spring framework. Spring is design and implemented so beautifully that you can extend any part of it’s features and inject your custom implementation out of the box. It is very well time tested and working flawless as today also.

3.1. A calculator program without OCP

Let’s say we are creating a simple calculator module with only two operations addition and subtraction. The code of the module is as below.

public interface IOperation {
}
public class Addition implements IOperation 
{
	private double firstOperand;
	private double secondOperand;
	private double result = 0.0;
	
	public Addition(double firstOperand, double secondOperand) {
		this.firstOperand = firstOperand;
		this.secondOperand = secondOperand;
	}

	//Setters and getters
}
public class Substraction implements IOperation 
{
	private double firstOperand;
	private double secondOperand;
	private double result = 0.0;
	
	public Substraction(double firstOperand, double secondOperand) {
		this.firstOperand = firstOperand;
		this.secondOperand = secondOperand;
	}

	//Setters and getters
}
public interface ICalculator {
	void calculate(IOperation operation);
}
public class SimpleCalculator implements ICalculator 
{
	@Override
	public void calculate(IOperation operation) 
	{
		if(operation == null) {
			throw new InvalidParameterException("Some message");
		}
		
		if(operation instanceof Addition) {
			Addition obj = (Addition) operation;
			obj.setResult(obj.getFirstOperand() + obj.getSecondOperand());
		} else if(operation instanceof Substraction) {
			Addition obj = (Addition) operation;
			obj.setResult(obj.getFirstOperand() - obj.getSecondOperand());
		} 
	}
}

Above module code looks good and serve the purpose. But when in client application, a developer want to add the capability of multiplication – he has no way out besides changing the SimpleCalculator class code inside method calculate(). This code is not OCP compliant.

3.2. OCP compliant code

Remember that abstract the functionality what changes in the application. In this calculator program, the code in calculate method will change with every incoming new operation support request. So we need to add abstraction in this method.

Solution is to delegate the responsibility of providing the calculation logic inside the operation itself. Each operation must have it’s own logic to get the result and operand. See how the modified code looks now.

public interface IOperation {
	void performOperation();
}
public class Addition implements IOperation 
{
	private double firstOperand;
	private double secondOperand;
	private double result = 0.0;
	
	public Addition(double firstOperand, double secondOperand) {
		this.firstOperand = firstOperand;
		this.secondOperand = secondOperand;
	}

	//Setters and getters

	@Override
	public void performOperation() {
		result = firstOperand + secondOperand;
	}
}
public class Substraction implements IOperation 
{
	private double firstOperand;
	private double secondOperand;
	private double result = 0.0;
	
	public Substraction(double firstOperand, double secondOperand) {
		this.firstOperand = firstOperand;
		this.secondOperand = secondOperand;
	}

	//Setters and getters

	@Override
	public void performOperation() {
		result = firstOperand - secondOperand;
	}
}
public interface ICalculator {
	void calculate(IOperation operation);
}
public class SimpleCalculator implements ICalculator 
{
	@Override
	public void calculate(IOperation operation) 
	{
		if(operation == null) {
			throw new InvalidParameterException("Some message");
		}
		
		operation.performOperation();
	}
}

Now we can add as many operations as we want without changing the original module code. Any new operation will fit in easily. e.g. multiplication operation will be written like this and will work correctly.

public class Multiplication implements IOperation 
{
	private double firstOperand;
	private double secondOperand;
	private double result = 0.0;
	
	public Multiplication(double firstOperand, double secondOperand) {
		this.firstOperand = firstOperand;
		this.secondOperand = secondOperand;
	}

	//Setters and getters

	@Override
	public void performOperation() {
		result = firstOperand * secondOperand;
	}
}

4. Conclusion

The open/closed principle is a guideline for the overall design of classes and interfaces and how developers can build code that allows change over time.

Now when most organisations are adopting agile practices, with each passing sprint, new requirements are inevitable and should be embraced. If the code you have produced up is not built to enable change, change will be difficult, time consuming, error prone, and costly.

By ensuring that your code is open to extension but closed to modification, you effectively disallow future changes to existing classes and assemblies, which forces programmers to create new classes that can plug into the extension points.

A suggested approach is to identify parts of the requirements that are likely to change or that are particularly troublesome to implement, and factor these out behind extension points.

Happy Learning !!

Read More:

Wikipedia

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

1 thought on “Open closed principle”

  1. Hii Lokesh, i appreciate your hard work to put every article simple. I appreciate the way u explained all the design patterns putting your experience on design patterns in a simple clear way.

    I am clear about Open/close principle.

    I work on spring boot web based projects. I prefer module based packaging over layer based packing so all the packages contains classes only related to that module feature.

    I keep no dependency between packages so that it helps me to change the class code of any package in future easily because there is no dependency between packages.

    After reading open close principle i got a lot of doubts in me. I used to follow adding new feature directly to existing classes of controller, service and repository. I need your clarification on below points to clear my doubts

    As u already know we write controllers, services and repositories separately, each class contains multiple methods. we don’t write a separate class which contain only single method which perform add operation and one more class which perform get operation to perform crud operations or business logics separately.

    1. How can i follow open/close principle when i have a new requirement to add new functionality to the module feature do i need to write a separate controller class service class and spring data repository ?
    Ex: user management module earlier i provided only add and get functionality and released to production. Know the requirement is to provide delete operation of user do i need to create a separete classes of all controller, service etc. Then extend it to previous controller, service class and add new functionality to newly created controller.

    2. what i should do when a new requirement comes to provide delete functionality to delete user in user management module. How to take up this new requirement and write a code for all layers which follows open/close principle. so that my code neer breaks any other code.

    3. What is the best way in spring MVC layer based application world to follow this Open/close Principle can u expain with exmpale in detail so it clarifies many developers.

    can u expain in detail these above mentioned points with example.

    Thank you in advance, I appreciate your work on sharing your knowledge wisdom.

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.