We will start with what Introduction and then explain the principle with an example.
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.
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
}
}
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);
}
}
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).
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?
Let's follow Open Close principle in ImageEditor
class along with Single Responsibility Principle.
public class ImageEditor {
public void drawShape (Shape s) {
s.draw();
}
// other methods related to editing image goes here
}
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
}
}
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
class. 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.