Tuesday, 28 April 2020

SOLID Principle

SOLID Priniciples - for code refactor and smells code means first identify wrost code, then apply SOLID principles to refactor.


S - Single Responsibility
O - Open for extension / Closed for Modification (Strategy Pattern )
L - Liskov Substutions
I  - Interface Segression
D - Dependency Inversion ( Template Method Pattern)

Here S-0 principle based on responsibilities and L - I based on split responsiblities.

S - Single Responsibility
    "A class should have only one reason to change"
Usecase: we need an object to keep an email message.
interface IEmail {
        public void setSender(String sender);
        public void setReceiver(String receiver);
        public void setContent(String content);
}

Email Responsibilites: setSender, setReceiver
Content Responsibilites: setContent (Currently supporting String content only)

Now we got new feature: we need to support html, pdf feature content. But our current Email supports String content type

Solution: Then we need to take more care String content convert it into interface like Below.
interface IContent {
      public String getAsString(); //used for serialize
}

Now our modified IEmail Interface:
interface IEmail {
        public void setSender(String sender);
        public void setReceiver(String receiver);
        public void setContent(IContent content);
}

class Email implments IEmail {
        public void setSender(String sender) {   //implment }
        public void setReceiver(String receiver) {   //implment }
        public void setContent(IContent content) {   //implment }
}



Open Close Principle : Open for extension but closed for modifications

Through bad coding to good coding we will demonstrate this example.

abstract class Shape {     class Rectangle extends Shape {        class Circle extends Shape {
    int m_type;                         Rectangle() {                                     Circle() {
}                                                    super.m_type = 1;                          super.m_type = 2;
                                         }  }                                                      }}

//Open-close priniciple bad example -
class GraphicEditor {
            public void drawShape(Shape s) {
                  if(s.m_type==1) {   drawRectangle() ; }
                  else if(s.m_type==2) { drawCircle(); }
              }
           
               //circle responsible behaviour
               public void drawCircle(Circle c) {       }
       
                //rectangle responsible behaviour
              public void drawRectangle(Rectangle r) {    }
}

In the bad code having rectangle and circle behaviours.
circle - behaviour - drawCircle();
rectangle - behaviour - drawRectangle();

Issue: if we add new shape then we need to change drawShape() and also add the new drawMehtod() into GraphicEditor.

Solution: In the new design we use abstract draw() method in GraphicEditor for drawing objects. draw() implmentation into the concrete shape objects.


abstract class Shape {     class Rectangle extends Shape {        class Circle extends Shape {
abstract void draw();       draw() {                                               draw() {
}                                                    //implment                                            //implment
                                         }  }                                                      }}


class GraphicEditor {
            public void drawShape(Shape s) {
                  s.draw();
            }
}


Now no changes required when a new shape is added (Good !!)

Please refer Strategy pattern which will suitable for this principle.







D - Dependency Inversion principle "Depend on Abstract/Interface not concrete classes"
I will explain 2 ways this principle

  • "High-level modules should not depend on low-level modules, both should depend on abstractions" style.

Class Manager {                                                                      Class Worker {
       Worker worker;                                                                          public void work() {
        public void setWorker(Worker worker) {                                      System.out.println("working");
              this.worker = worker;                                                          }
        }                                                                                        }
         public void manager() {
                  worker.work();
         }
}

Now company introduced new specialized worker then we need to change Manager class also
Class SuperWorker {
              public void work() {
                  System.out.println("super working");
              }
}

Solution: Now Design a new Abstraction Layer is added through the IWorker interface.

interface IWorker {   public void work() ; }

class Worker implements IWorker {     public void work() { System.out.println("working"); } }

class SuperWorker implements IWorker { public void work() { System.out.println("super working"); } }


Class Manager {
          IWorker worker;
          public void setWorker(IWorker worker) {
                   this.worker = worker;
          }
          public void manager() {
                  worker.work();
           }
}


  • "Concrete classes should depend on interfaces and abstract classes because they change less often and more higher level modules" style.

In this template method design pattern concrete classes depend on parent class House method buildHouse(), this parent method changes very less often.






   
     


     


No comments:

Post a Comment