Open Close Principle with example in Java

Open close principle

  • Bertrand Meyer coined the term open close principle in his 1988 book Object Oriented Software Construction.
  • Software entities like classes, modules and functions should be "open for extension but closed for modifications".
  • It is a generic principle. You can consider it when writing your classes to make sure that when you need to extend their behaviour you don't have to change the class but to extend it.
  • When referring to the classes Open Close principle can be ensured by the use of Abstract classes and/ or Interfaces and concrete classes implementing their behaviour. This will enforce having concrete classes implementing Abstract classes/ Interfaces instead of changing them. 
  • Some particular cases where this principle is used are Template Design Pattern, Strategy design pattern.

We want to draw different kind of images. For this, We wrote a generic class ImageEditer which can draw shapes. See the below code snippet.

package javawithgaurav.openclose;

/**
 * 
 * @author Gaurav Rai Mazra
 * <a href="www.javawithgaurav.blogspot.in">Click here to view more</a>
 */
//Abstract class different type of Shape
abstract class Shape {
    public static final int TYPE_RECTANGLE = 1;
    public static final int TYPE_SQUARE = 2;
    
    private int type;
    
    Shape (int type) {
        
    }
    
    public int getType() {
        return type;
    }
}

//Rectangle
class Rectangle extends Shape {
    Rectangle () {
        super(TYPE_RECTANGLE);
    }
}

//Square
class Square extends Shape {
    Square () {
        super(TYPE_SQUARE);
    }
}
package javawithgaurav.openclose;

/**
 * 
 * @author Gaurav Rai Mazra
 * <a href="www.javawithgaurav.blogspot.in">Click here to view more</a>
 */

public class ImageEditor
{
    public void drawShape (Shape s) {
        final int shapeType = s.getType();
        //Based on shape type draw shapes code
        if (shapeType == Shape.TYPE_RECTANGLE) {
           drawRectangle(s); 
        }
        else if (shapeType == Shape.TYPE_SQUARE) {
            drawSquare(s);
        }
    }
    
    private void drawRectangle(Shape s) {
        // Logic to draw Rectangle
    }
    
    private void drawSquare(Shape s) {
        // logic to draw Square
    }
}

By looking into above code snippet, we see no problem. We have Shape as abstract class and then its concrete implementations like Rectangle, Square. And, we have ImageEditor class who have only exposing drawShape() to draw shape of any type and it is hiding method to draw specific image like Rectangle, Square etc. Atleast, we are using abstraction, encapsulation, hiding etc. features of OOPs(pun intended).

Problem with above structure

In case we are required to add new shape say Polygon then what?. Will our ImageEditor be able to draw it? Answer is No.

We need to change ImageEditor class to support this behavior. It means we need to modify it??? If we modify it then we need to unit test it. One change can raise some other issue in class.

So, how we can ensure to close ImageEditor for modifications?

Good approach

We will change it according to OCP and also SRP. Let's do it in the code snippet below.

package javawithgaurav.openclose;

/**
 * 
 * @author Gaurav Rai Mazra
 * <a href="www.javawithgaurav.blogspot.in">Click here to view more</a>
 */
//Abstract class different type of Shape
abstract class Shape {
  private String name;

  Shape (String name) {
      this.name = name;
  }
  
  public String getName() {
      return name;
  }
  
  abstract void draw();
}

//Rectangle
class Rectangle extends Shape {
  Rectangle () {
      super("Rectangle");
  }
  
  @Override
  public void draw() {
      //logic to draw RECTANGLE
  }
}

//Square
class Square extends Shape {
  Square () {
      super("Square");
  }
  
  @Override
  public void draw() {
      //logic to draw SQUARE
  }
}
package javawithgaurav.openclose;

/**
 * 
 * @author Gaurav Rai Mazra
 * <a href="www.javawithgaurav.blogspot.in">Click here to view more</a>
 */

public class ImageEditor
{
    public void drawShape (Shape s) {
        s.draw();
    }
    // other methods related to editing image goes here
}

We declared the responsibility to draw in Shape but also make it out abstract so that every concrete class should define how to draw itself.

In the ImageEditor class, the drawShape() delegate the call to draw to Shape. Now, with this change we can draw any kind of shape and in future if it has to draw new shape then we don't have to modify it.

No comments :

Post a Comment