Showing posts with label SOLID Principles. Show all posts
Showing posts with label SOLID Principles. Show all posts

Single Responsibility Principle

Single Responsibility Principle states that there should be only one reason for a class to change. This principle states that if we have two reasons to change for a class, we have to split the functionality in two different classes. Each class will handle only one responsibility and on future if we need to make one change we are going to make it in the class which handle it. When we need to make a change in a class having more than one responsibilities the change might affect the other functionality of the classes.

Single Responsibility Principle states that: A class should have only one reason to change.

Every responsibility of a class is an area of potential change. More than one responsibility means more than one area of change. This principle guides us to keep each class to a single responsibility. 


The Single Responsibility Principle represents a good way of identifying classes during the design phase of an application and it reminds you to think of all the ways a class can evolve. A good separation of responsibilities is done only when the full picture of how the application should work is well understand.
 


Example

 
Let's assume we need an object to keep an email message. We are going to use the IEmail interface from the below sample. At the first sight everything looks just fine. At a closer look we can see that our IEmail interface and Email class have 2 responsibilities (reasons to change). One would be the use of the class in some email protocols such as pop3 or imap. If other protocols must be supported the objects should be serialized in another manner and code should be added to support new protocols. Another one would be for the Content field. Even if content is a string maybe we want in the future to support HTML or other formats.
 
If we keep only one class, each change for a responsibility might affect the other one:
 
Adding a new protocol will create the need to add code for parsing and serializing the content for each type of field.
Adding a new content type (like html) make us to add code for each protocol implemented.


// single responsibility principle - bad example
interface IEmail {
 public void setSender(String sender);
 public void setReceiver(String receiver);
 public void setContent(String content);
}

class Email implements IEmail {
 public void setSender(String sender) {// set sender; }
 public void setReceiver(String receiver) {// set receiver; }
 public void setContent(String content) {// set content; }
}



We can create a new interface and class called IContent and Content to split the responsibilities. Having only one responsibility for each class give us a more flexible design:
adding a new protocol causes changes only in the Email class.
adding a new type of content supported causes changes only in Content class.



// single responsibility principle - good example
interface IEmail {
 public void setSender(String sender);
 public void setReceiver(String receiver);
 public void setContent(IContent content);
}

interface Content {
 public String getAsString(); // used for serialization
}

class Email implements IEmail {
 public void setSender(String sender) {// set sender; }
 public void setReceiver(String receiver) {// set receiver; }
 public void setContent(IContent content) {// set content; }
}


 

Conclusion


The Single Responsibility Principle represents a good way of identifying classes during the design phase of an application and it reminds you to think of all the ways a class can evolve. A good separation of responsibilities is done only when the full picture of how the application should work is well understand. 

 

Open Close Design Principle

The Open Close Principle states that the design of the code should be done in such a way that new functionality should be added with minimum changes in the existing code. The design should be done in such a way to allow the adding of new functionality as new classes and keeping the existing classes as unchanged as possible.

Open Close Principle states that Classes should be Open for Extension, but Closed for Modification.

The goal of open close principle is to allow classes to be easily extended to incorporate new behavior without modifying existing code.


Like any other design principle Open Close Principle is also a design principle which intends to make a flexible design. As we know that making flexible design involves some additional time and effort for it and it also introduce new level of abstraction which increases the complexity of the code. So this principle should be applied in those area which include frequent changes. 

There are many design patterns which are based on open close principle. Such Design Patterns help us to extend code without changing existing code. For example the Decorator pattern is the best example of Open Close design principle.
 


Example

 
Bellow is an example which violates the Open Close Principle. It implements a graphic editor which handles the drawing of different shapes. It's obviously that it does not follow the Open Close Principle since the GraphicEditor class has to be modified for every new shape class that has to be added. There are several disadvantages:
for each new shape added the unit testing of the GraphicEditor should be redone.
when a new type of shape is added the time for adding it will be high since the developer who add it should understand the logic of the GraphicEditor.
adding a new shape might affect the existing functionality in an undesired way, even if the new shape works perfectly
 
In order to have more dramatic effect, just imagine that the Graphic Editor is a big class, with a lot of functionality inside, written and changed by many developers, while the shape might be a class implemented only by one developer. In this case it would be great improvement to allow the adding of a new shape without changing the GraphicEditor class.
 
 
// Open-Close Principle - Bad example
 class GraphicEditor {

  public void drawShape(Shape s) {
   if (s.m_type==1)
    drawRectangle(s);
   else if (s.m_type==2)
    drawCircle(s);
  }
  public void drawCircle(Circle r) {....}
  public void drawRectangle(Rectangle r) {....}
 }

 class Shape {
  int m_type;
 }

 class Rectangle extends Shape {
  Rectangle() {
   super.m_type=1;
  }
 }

 class Circle extends Shape {
  Circle() {
   super.m_type=2;
  }
 }
 
 
Bellow is a example which supports the Open Close Principle. In the new design we use abstract draw() method in GraphicEditor for drawing objects, while moving the implementation in the concrete shape objects. Using the Open Close Principle the problems from the previous design are avoided, because GraphicEditor is not changed when a new shape class is added:
no unit testing required.

no need to understand the sourcecode from GraphicEditor.
since the drawing code is moved to the concrete shape classes, it's a reduced risk to affect old functionallity when new functionallity is added.
 
// Open-Close Principle - Good example
 class GraphicEditor {
  public void drawShape(Shape s) {
   s.draw();
  }
 }

 class Shape {
  abstract void draw();
 }

 class Rectangle extends Shape  {
  public void draw() {
   // draw the rectangle
  }
 }
 
 

Conclusion

 
Like every principle OCP is only a principle. Making a flexible design involves additional time and effort spent for it and it introduce new level of abstraction increasing the complexity of the code. So this principle should be applied in those area which are most likely to be changed.
There are many design patterns that help us to extend code without changing it. For instance the Decorator pattern help us to follow Open Close principle. Also the Factory Method or the Observer pattern might be used to design an application easy to change with minimum changes in the existing code.