Design Pattern Principles - Interface Segregation Principle

an official definition

The Interface Segregation Principle (Interface Segregation Principle), also known as the ISP principle, is officially defined as:

  1. Clients should not be forced to depend upon interfaces that they don't use.
    (client should not depend on interfaces it doesn't need)
  2. The dependency of one class to another one should depend on the smallest possible
    interface. (The dependencies between classes should be established on the smallest interface)

basic introduction
In layman's terms, don't define too many methods in an interface, the interface should be as detailed as possible

Two case presentations

Suppose there is such a case scenario. Now there is an interface knife. Given that he has three abilities, he can cut apples, tomatoes, and potatoes. Two classes Chef Zhang and Chef Li have these abilities respectively. There is a fruit shop class. Suppose It needs Master Zhang to cut apples and tomatoes, while another vegetable store needs Master Li to cut tomatoes and potatoes

common plan

public class SegregationDemo {
    public static void main(String[] args) {

        //fruit shop
        new FruitShop().cutApple(new CookZhang());
        new FruitShop().cutTomato(new CookZhang());

        //Vegetable store
        new VegetableShop().cutTomato(new CookLi());
        new VegetableShop().cutPotato(new CookLi());
    }
}
// Define the interface knife
interface Knife {
    //ability to slice apples
    void cutApple();

    //ability to cut tomatoes
    void cutTomato();

    //ability to cut potatoes
    void cutPotato();
}

//Chef Zhang
class CookZhang implements Knife {
    @Override
    public void cutApple() {
        System.out.println("Chef Zhang is cutting apples");
    }
    @Override
    public void cutTomato() {
        System.out.println("Chef Zhang is cutting tomatoes");
    }

    @Override
    public void cutPotato() {
        System.out.println("Chef Zhang is cutting potatoes");
    }
}
//Lee Chef Class
class CookLi implements Knife {
    @Override
    public void cutApple() {
        System.out.println("Chef Li is cutting apples");
    }

    @Override
    public void cutTomato() {
        System.out.println("Chef Li is cutting tomatoes");
    }

    @Override
    public void cutPotato() {
        System.out.println("Chef Li is cutting potatoes");
    }
}

/*Suppose there is such a case scenario, now there is an interface knife, given that he has three capabilities, can cut apples, tomatoes, potatoes, two classes
 Chef and Chef Li have these abilities respectively. There is a fruit store class, assuming that Master Zhang is needed to cut apples and tomatoes, and another vegetable store class needs
 Master Li is here to cut tomatoes and potatoes*/

//fruit shop
class FruitShop {
    // Pass the interface type as a parameter
    public void cutApple(Knife knife) {
        knife.cutApple();
    }

    public void cutTomato(Knife knife) {
        knife.cutTomato();
    }
}

//Vegetable store
class VegetableShop {

    public void cutTomato(Knife knife) {
        knife.cutTomato();
    }

    public void cutPotato(Knife knife) {
        knife.cutPotato();
    }
}

case analysis


1. VegetableShop uses two methods in Knife, and the same is true for FruitShop
2. CookZhang and CookLi respectively implement the Knife interface. Knifes is not the smallest interface for CookZhang and CookLi.
In other words, CookZhang and CookLi must implement methods they don't need. Caused this class to be coupled
This violates the interface segregation principle (dependencies between classes should be established on the smallest interface)

solution

solution

According to the principle of interface isolation, we split Knife into three independent interfaces, isn't it all right?
step:

  1. Split Knife into three independent interfaces: AppleKnife, TomatoKinfe, and PotatoKnife
  2. CookZhang (Zhang chef class) implements AppleKnife, TomatoKinfe
  3. CookLi (Lee cook class) implements TomatoKinfe, PotatoKnife
  4. FruitShop (fruit shop class) cutApple method and cutTomato method are respectively passed to AppleKnife and TomatoKinfe
  5. VegetableShop (vegetable shop class) cutTomato method and cutPotato method are respectively passed to TomatoKinfe and PotatoKnife
public class SegregationDemo {
    public static void main(String[] args) {
        //fruit shop
        new FruitShop().cutApple(new CookZhang());
        new FruitShop().cutTomato(new CookZhang());
        //Vegetable store
        new VegetableShop().cutTomato(new CookLi());
        new VegetableShop().cutPotato(new CookLi());
    }
}

//Example interface segregation principle
// Split interface knife into three separate interfaces
interface AppleKnife {
    //ability to slice apples
    void cutApple();

}
interface TomatoKinfe{
    //ability to cut tomatoes
    void cutTomato();
}
interface PotatoKnife{
    //ability to cut potatoes
    void cutPotato();
}

//Chef Zhang
class CookZhang implements AppleKnife,TomatoKinfe {

    @Override
    public void cutApple() {
        System.out.println("Chef Zhang is cutting apples");
    }

    @Override
    public void cutTomato() {
        System.out.println("Chef Zhang is cutting tomatoes");
    }


}

//Lee Chef Class
class CookLi implements TomatoKinfe,PotatoKnife {

    @Override
    public void cutTomato() {
        System.out.println("Chef Li is cutting tomatoes");
    }

    @Override
    public void cutPotato() {
        System.out.println("Chef Li is cutting potatoes");
    }
}

/*Suppose there is such a case scenario, now there is an interface knife, given that he has three capabilities, can cut apples, tomatoes, potatoes,
Chef Zhang and Chef Li have these abilities respectively. There is a fruit shop class, assuming that Master Zhang is needed to cut apples and tomatoes, and another vegetable shop class needs
 Master Li is here to cut tomatoes and potatoes*/
//fruit shop
class FruitShop {
    // Pass the interface type as a parameter
    public void cutApple(AppleKnife knife) {
        knife.cutApple();
    }

    public void cutTomato(TomatoKinfe knife) {
        knife.cutTomato();
    }
}

//Vegetable store
class VegetableShop {

    public void cutTomato(TomatoKinfe knife) {
        knife.cutTomato();
    }

    public void cutPotato(PotatoKnife knife) {
        knife.cutPotato();
    }
}

Summary

The principle of interface isolation is that when a class relies on (uses) another class through an interface, it is necessary to ensure that the interface it depends on is the smallest.
If there are methods in the interface that are not used, they are isolated, and the method of isolation is to split the original interface into the smallest granularity to avoid coupling

Three comparison with the single responsibility principle

Single responsibility principle: Reasonable responsibility decomposition, a class is only responsible for one responsibility
Interface Segregation Principle: Dependencies between classes should be established on the smallest interface

Same point
Both require the structure to be split, both require smaller granularity, and both want to reduce coupling

difference
different viewing angles

Single Responsibility Principle: classes and interfaces have a single responsibility, focusing on responsibility
Interface isolation principle: we are required to use multiple specialized interfaces as much as possible, focusing on interface design

When we use the interface isolation principle for interface splitting, we must follow the single responsibility principle

Tags: Java Design Pattern

Posted by paggard on Tue, 20 Dec 2022 01:28:38 +0300