Tech Point Fundamentals

Sunday, January 2, 2022

Open Closed Principle of SOLID | OCP

Open Closed Principle of SOLID | OCP

Open-Closed-Principle

SOLID Principles are one of the mandatory question in each and every interview. This is the third part of the SOLID Design Principle. In this part, we will walk through the second SOLID Design Principle i.e Open Closed Principle (OCP) in detail with live real examples. 


Please read the previous parts over here below links:


SOLID Design Principles

Single Responsibility Principle


Watch our videos here




Introduction


In the SOLID acronym, the second letter "O" represents the "Open Closed Principle"The Open-Closed Principle focuses on the behavior of a class or module in case of modification and extension.


When a single change in a program results in a cascade of changes to the dependent modules it is considered a "bad design". The program becomes fragile, rigid, unpredictable, and unreusable.  The open-closed principle is the root motivation behind many of the heuristics and conventions that have been published regarding OOD over the years.


This principle states that you should design modules that never change. When requirements change, you extend the behavior of such modules by adding new code, not by changing old code that already works.


The Open-Closed Principle encourages the developers to design and write the code in such a way that adding new functionality would involve minimal changes to existing code. Most of the changes should be handled in new methods and new classes only instead of the original one.




Open-Closed Principle (OCP)


The Open-Closed Principle is the second SOLID Design Principle. The Open-Closed Principles is first proposed by Bertrand Meyer in his book  "Object-Oriented Software Construction" in the year 1988 but it was updated by Robert C. Martin later in the 1990s.


As per  Ivar Jacobson in his book "Object-Oriented Software Engineering a Use Case Driven Approach":


"All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version."


How can we create designs that are stable in the face of change and that will last longer than the first version? So Bertrand Meyer gave us guidance to achieve this in his Open Closed Principle.




Definition of OCP


The Open-Closed Principle said that classes should be open for extension and closed to modification. Here modification means changing the code of an existing class, and extension means adding new functionality.


According to Bertrand Meyer, the Open-Closed Principle states that:


Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.


Here Modification means changing the code of an existing class, module, or function, and Extension means adding new functionality or behavior.  


In other words:


"Class should be extendable without modifying the class itself."


But here the "extension" does not mean the extending in java or inheritance in C# only at all. This definition is given before the evolution of C++, Java, and C#. What it means here is that a module should provide extension points to alter its behavior. 


So, we should be able to add new functionality without touching the existing code for the class or module because whenever we modify the existing code, we are taking the risk of creating potential bugs. 


Modules following the Open-Closed Principle must have two primary attributes:

1.  They are “Open For Extension”: The behavior of the module can be extended i.e we can make the module behave in new and different ways as the requirements of the application change.

2. They are “Closed for Modification”: The source code of such a module is inviolate i.e No one is allowed to make source code changes to it.


How can we change the behavior of a module without making changes to it? The statement is looking contradictory but not actually. The solution is an abstraction.


Please watch the Fragile Base Class Problem video here.




Mayer's Open Closed Principle: Inheritance Based OCP 


Mayer provides the Open-Closed Principle based on inheritance so the actual definition given by Mayer is Inheritance Based Open-Closed Principle. So he proposed to use inheritance to achieve this goal.


Since Bertrand Mayer proposes the Inheritance Based Open-Closed Principle to achieve this goal, and inheritance introduces tight coupling if the subclasses depend on implementation details of their parent class.


So the concrete class inheritance leads to a lot of problems, which is realized by Robert C. Martin. So he proposed a new version of the Open-Closed Principle definition in his book "Object-Oriented Software Construction".




Martin's Open Closed Principle: Polymorphic OCP 


Robert C. Martin redefined the Open-Closed Principle to the Polymorphic Open-Closed Principle


In contrast to Meyer's definition, this definition advocates inheritance from abstract base classes or interfaces. Interface specifications can be reused through inheritance but implementation need not be. 


You should be able to extend the behavior of a system without having to modify that system.


Please read the Polymorphic Dilemma over here:


Method Overloading and Overloading Dilemma - Part 1

Method Overloading and Overloading Dilemma - Part 2

Method Overloading and Overloading Dilemma - Part 3





Explanation


module or class is said to be open if it is still available for the extension means new fields, properties, or functions can be added on which it performs.


module or class is said to be closed if it is available only for use by other modules or classes means the module has been given a well-defined, stable description.


A class is closed, since it may be compiled, stored in a library, baselined, and used by many client classes. But at the same time, it is also open, since any new class may use it as a parent class, adding new features into it. By doing so, there is no need to change the original base class or to disturb its clients.


Similarly, the Point of Variation Principle (PV) also states that 

"Identify points of predicted variation and create a stable interface around them."


There are the following guidelines to implement this OCP principle in an easy way:


1. Always add the new functionalities by creating new derived classes which should be inherited from the original base abstract class.

2. Always allow the client to access the original class with an abstract interface.

3. Always on the new requirement instead of touching the existing class, it’s always better and suggested to create new derived classes.




Implementation Guidelines for Open-Closed Principle:


So during the 1990s, the Open-Closed Principle was redefined to use abstracted interfaces, where the implementations can be changed and multiple implementations could be created and polymorphically substituted for each other.


The abstractions are abstract base classes or interfaces, and the unbounded group of possible behaviors is represented by all the possible derivative classes that implement them. Such a module can be closed for modification since it depends upon an abstraction that is fixed.  The behavior of that module can be extended by creating new derivatives of the abstraction and it is possible for a derived module to manipulate an abstraction.


But the Open-Closed Principle can also be achieved in many other ways, including through the use of inheritance or through composition or composite design patterns like the Strategy Pattern.


Please watch the Inheritance vs Composition video here.


One way is to make use of polymorphism to invoke extended behaviors of an object at run time. It also uses interfaces instead of SuperClasses to allow different implementations which you can easily substitute without changing the code that uses them. 


The existing interface is closed to modifications and new implementations must, at least minimum, implement that interface. The interfaces are closed for modifications, and you can provide new implementations to extend the functionality of your software. But no significant program can be 100% closed.




Heuristics and Conventions for OCP:


Since the open-closed principle is the root motivation behind many of the heuristics and conventions that have been published regarding OOD over the years. Some of them are as follows:


1. All the Member Variables must be Private


Member variables should never be known to any other class, including derived classes. So they should be declared private, rather than public or protected

The reason behind this in the context of OCP is that when the member variables of a class change, every function that depends upon those variables must be changed. 

In any OOD, we expect that the methods of a class should never be closed to changes in the member variables of that class. However, we do expect that any other class, including subclasses, is closed against changes to those variables. We have a name for this expectation, we call it: encapsulation. 




2. No Global Variables Ever


The argument against global variables is similar to the argument against public member variables.  Any module that uses the variable in a way that the other modules don’t expect, will break those other modules.


3. No Run Time Type Identification (RTTI) 


In OOD the dynamic casting or any form of run-time type identification (RTTI) is intrinsically dangerous and should be avoided.  As a general rule of thumb, if the use of RTTI does not violate the open-closed principle, it is safe.



Example


 

public class Report
{
	
   public TemplateBody GetTemplate(string clientName, ReportType reportType)
   {

	TemplateBody template = new TemplateBody();
    
  	if (reportType == ReportType.FullReport)
  	{
  	    template.TemplateType = TemplateType.TemplateFull;
        template.ClientName = clientName;
        template.Date = DateTime.Now;
  	}
  	else if (reportType == ReportType.PartialReport)
  	{
  	     template.TemplateType = TemplateType.TemplatePartial;
         template.ClientName = clientName;
         template.Date = DateTime.Now;
  	}
  	
  	return template;
  }
}

public enum ReportType
{
  FullReport = 1,
  PartialReport = 2
};

public enum TemplateType
{
   TemplateFull = 1,
   TemplatePartial = 2
};

public class TemplateBody
{
   public TemplateType TemplateType { get; set; }
   public string ClientName { get; set; }
   public DateTime Date { get; set; }
}

		

As of now, we have only two report types and template types so we have used if-else for this implementation, but in the future, if a new report type came how do you incorporate that? 

You have to change this class and add one more else-if condition. So here as we are changing the source code for the new requirement, we are violating the Open-Closed principle.  



As per the Open-Closed Principle, instead of modifying, we should go for the extension. Let's do that and when a new invoice type needs to be added, then we need to add a new class. As a result, the current functionalities that are already implemented are going to be unchanged. 



interface IReport
{
   TemplateBody GetTemplate(string clientName);
}

 public class FullReport : IReport
 {
   public TemplateBody GetTemplate(string clientName)
   {
   	 TemplateBody template = new TemplateBody();

   	 template.TemplateType = TemplateType.TemplateFull;
   	 template.ClientName = clientName;
   	 template.Date = DateTime.Now;

   	 return template;
   }
}

 public class PartialReport : IReport
 {
   public TemplateBody GetTemplate(string clientName)
   {
     TemplateBody template = new TemplateBody();

     template.TemplateType = TemplateType.TemplatePartial;
     template.ClientName = clientName;
     template.Date = DateTime.Now;

     return template;
   }
}



Now the interface IReport is closed for modification whenever a new requirement comes, we can extend it and implement the new behavior. You can also use the abstract class and dynamic polymorphism to achieve the same.


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. By ensuring that your code is open to extension but closed to modification in the future.


No comments:

Post a Comment

Please do not enter any HTML. JavaScript or spam link in the comment box.