Wednesday 5 September 2018

Open-closed principle design problem

Suppose a drawing application is implemented like below.

Shape.java
package com.sample.model;

public enum Shape {
 CIRCLE("circle"), SQUARE("square"), RECTANGLE("rectangle");

 private Shape(String type) {
  this.type = type;
 }

 private String type;

 public String getType() {
  return type;
 }

}

Drawer.java
package com.sample.util;

import com.sample.model.Shape;

public class Drawer {
 private Shape shape;

 public Shape getShape() {
  return shape;
 }

 public void setShape(Shape shape) {
  this.shape = shape;
 }

 public void draw() {
  if (Shape.CIRCLE.getType().equals(shape.getType())) {
   System.out.println("Drawing Circle");
  } else if (Shape.RECTANGLE.getType().equals(shape.getType())) {
   System.out.println("Drawing Rectangle");
  } else if (Shape.SQUARE.getType().equals(shape.getType())) {
   System.out.println("Drawing Square");
  }
 }
}

Application.java

package com.smaple.app;

import com.sample.model.Shape;
import com.sample.util.Drawer;

public class Application {
 public static void main(String args[]) {
  Drawer drawer = new Drawer();
  
  drawer.setShape(Shape.SQUARE);
  drawer.draw();
  
 }
}

Above code breaks the design principle “Open to extension, closed to modification”.

As per Open-closed principle, software entities such as classes, methods should be open for extension and closed for modification. That is we can extend the behavior of software entity, without touching the source code of the entity.

For example, there is a class ‘ConnectionFactory’, it provides number of methods to connect to databases like MySQL, MongoDB, Elasticsearch etc., Assume there are number of classes using this ConnectionFactory. Even if you change single line in ConnectionFactory class, you need to retest all the modules that are using ConnectionFactory class. If you design your code like this, you will end up in testing all the dependent modules often.

Open-closed principle solved this problem in straightforward way.

Rule 1: Design a module such that, it never change.

Rule 2: When requirements change, extend (Inheritance) the behavior of an entity by adding new code.

Modules that follow above two rules are open for extension and closed for modification.

Let’s redesign the drawing application like below.


Shape.java
package com.sample.model;

public interface Shape {
 void draw();
}

Circle.java
package com.sample.model;

public class Circle implements Shape{

 public void draw() {
  System.out.println("Drawing Circle");
 }

}

Square.java

package com.sample.model;

public class Square implements Shape {

 public void draw() {
  System.out.println("Drawing Square");
 }

}


Rectangle.java

package com.sample.model;

public class Rectangle implements Shape {

 public void draw() {
  System.out.println("Drawing Rectangle");
 }

}

Drawer.java

package com.sample.util;

import com.sample.model.Shape;

public class Drawer {
 private Shape shape;

 public Shape getShape() {
  return shape;
 }

 public void setShape(Shape shape) {
  this.shape = shape;
 }

 public void draw() {
  if (shape == null) {
   return;
  }
  shape.draw();
 }
}


Application.java

package com.smaple.app;

import com.sample.model.Square;
import com.sample.util.Drawer;

public class Application {
 public static void main(String args[]) {
  Drawer drawer = new Drawer();
  
  drawer.setShape(new Square());
  drawer.draw();
  
 }
}




No comments:

Post a Comment