Friday, 17 February 2017

Interface-segregation principle


What is the point in selling a horse saddle for one who does not own a horse?

The interface segregation principle (ISP) states that no client should be forced to depend on methods it does not use. ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them.

Clients should not be forced to depend upon methods that they don't use.



ISP is intended to keep a system decoupled and thus easier to refactor, change, and redeploy.

When we design an application we should take care how we are going to make abstract a module which contains several submodules. Considering the module implemented by a class, we can have an abstraction of the system done in an interface. But if we want to extend our application adding another module that contains only some of the sub-modules of the original system, we are forced to implement the full interface and to write some dummy methods. Such an interface is named fat interface or polluted interface. Having interface pollution is not a good solution and might induce inappropriate behavior in the system.



//interface segregation principle - bad example
interface IWorker {
    
     public void work();
     public void eat();
}

class Worker implements IWorker {
     public void work() {
           // ....working
     }
     public void eat() {
           // ...... eating in lunch break
     }
}

class Robot implements IWorker {
     public void work() {
           //.... working much more
     }

     /** Dummy implementation while robot will not eat lunch.*/
     public void eat() {
           //.... eating in lunch break
     }
}

class Manager {
     IWorker worker;

     public void setWorker(IWorker w) {
           worker=w;
     }

     public void manage() {
           worker.work();
     }
}

If the design is already done fat interfaces can be segregated using the Adapter pattern.



// interface segregation principle - good example
interface IWorker extends IFeedable, IWorkable {
}

interface IWorkable {
     public void work();
}

interface IFeedable{
     public void eat();
}

class Worker implements IWorkable, IFeedable {
     public void work() {
           // ....working
     }

     public void eat() {
           //.... eating in lunch break
     }
}

class Robot implements IWorkable {
     public void work() {
           // ....working
     }
}

class Manager {
     IWorker worker;

     public void setWorker(IWorker w) {
           worker=w;
     }

     public void manage() {
           worker.work();
     }
}

Thursday, 16 February 2017

Liskov’s Substitution Principle - A SOLID principle


Derived types must be completely substitutable for their base types.

Explanation:
To utilize the principles like inheritance, we create some class hierarchies (=extends some classes creating some derived classes).

LSP states that if a program module is using a Base class, then the reference to the Base class can be replaced with a Derived class without affecting the functionality of the program module i.e. we must make sure that the new derived classes just extend without replacing the functionality of old classes.

Otherwise the new classes can produce undesired effects when they are used in existing program modules.

package core.solid;
/**
 * Violation of Liskov's Substitution Principle
 */
class Rectangle {
     protected int width;
     protected int height;

     public void setWidth(int width){
           this.width = width;
     }

     public void setHeight(int height){
           this.height = height;
     }

     public int getWidth(){
           return width;
     }

     public int getHeight(){
           return height;
     }

     public int getArea(){
           return width * height;
     }   
}

class Square extends Rectangle {

     public void setWidth(int width){
           this.width = width;
           this.height = width;
     }

     public void setHeight(int height){
           this.width = height;
           this.height = height;
     }
}

public class LSPTest {

     private static Rectangle getNewRectangle() {
           /**
            * It can be an object returned by some factory.
            */
           return new Square();
     }

     public static void main (String args[]) {
          
           Rectangle rObj = LSPTest.getNewRectangle();

           /**
            * User knows that rObj it's a rectangle.
            * It assumes that he's able to set the width
            * and height as for the base class
            *
            * But the height and width are setter has
            * been changed as per the SQUARE property.
            **
            */
           rObj.setWidth(5);
           rObj.setHeight(10);

          
           /**
            * Unexpected result as the area of SQUARE calculated but
            * he was expecting the area of rectangle.
            **/
           System.out.println(rObj.getArea());
     }
}

Rectangle and Square two classes are used in the Application. We extend the application and add the Square class. The square class is returned using a factory pattern, based on some conditions and we don't know the exact what type of object will be returned. But we know it's a Rectangle.

We get the rectangle object, set the width to 5 and height to 10 and get the area. For a rectangle with width 5 and height 10 the area should be 50. Instead the result will be 100(=because rectangle class is Substituted by the Square class).

Related Posts Plugin for WordPress, Blogger...