设计模式原则:接口隔离原则

Published on 2024-02-19 17:28 in 分类: 博客 with 狂盗一枝梅
分类: 博客

一、接口隔离原则定义

接口隔离原则(Interface Segregation Principle),又称为ISP原则,官方定义如下

1、Clients should not be forced to depend upon interfaces that they don't use.(客户端不应该依赖它不需要的接口。)
2、The dependency of one class to another one should depend on the smallest possible interface.(类间的依赖关系应该建立在最小的接口上。)

这是站在客户端的角度上阐述了自己依赖的接口应该是什么样的:它应该尽量小,小到恰好能满足我需求的程度,多一点都不好;如果我不需要依赖这个接口,那我就不应该依赖它(我这解释似乎有点废话,嘿嘿)。

二、一个例子

1、班委选举

班级里要选举班长,能文能武的学生才能担当,那我抽象出来一个接口用来描述能文能武的人

public interface ISuperMan {
    
    //能武
    void canRun();
    
    //能文
    void canWrite();
}

班长实现该类

public class SquadLeader implements SuperMan{
    @Override
    public void canRun() {
        System.out.println("我是班长,我能跑");
    }

    @Override
    public void canWrite() {
        System.out.println("我是班长,我能写");
    }
}

还要选举语文课代表,语文课代表只需要“能文”

public class ChineseClassRepresentative implements SuperMan{
    @Override
    public void canRun() {
        
    }

    @Override
    public void canWrite() {
        System.out.println("我是语文课代表,我能写");
    }
}

还要选举体育课代表,体育课代表只需要“能武”

public class PhysicalEducationClassRepresentative implements SuperMan{
    @Override
    public void canRun() {
        System.out.println("我是体育课代表,我能跑");
    }

    @Override
    public void canWrite() {

    }
}

然后找个场景将其串联起来,班长、语文课代表、体育课代表依次发言

public class Main {

    public static void main(String[] args) {
        //班长发言
        SquadLeader squadLeader = new SquadLeader();
        squadLeader.canRun();
        squadLeader.canWrite();
        
        //语文课代表发言
        ChineseClassRepresentative chineseClassRepresentative = new ChineseClassRepresentative();
        chineseClassRepresentative.canWrite();
        
        //体育课代表发言
        PhysicalEducationClassRepresentative physicalEducationClassRepresentative = new PhysicalEducationClassRepresentative();
        physicalEducationClassRepresentative.canRun();
    }
}

运行下

我是班长,我能跑
我是班长,我能写
我是语文课代表,我能写
我是体育课代表,我能跑

程序可以运行,运行结果也没问题。只是语文课代表和体育课代表都不得不引入多余的方法实现,违反了设计模式的接口隔离原则:客户端不应该依赖它不需要的接口

上述代码的UML类图如下

image-20240219165320320

2、规则变化和重构

有一天,选举规则变更,要求班长能文能武还得英语好,那该怎么办?有两种方案

第一种方法: SuperMan接口新增方法canEnglish,班长类实现该方法。这种方法在SuperMan接口中新增一个方法,那语文课代表类和体育课代表类都得实现这个方法,能用,但是似乎不合适。

第二种方法: 新增一个接口,里面包含方法canEnglish,让班长类实现该新接口。这种方法单独新增一个接口,并声明方法canEnglish,班长类直接实现该接口就可以了,不会影响语文课代表类和体育课代表类,这个方法比第一种方法很明显更好。等等。。。既然如此,那canRun和canWrite是否也应该抽象出来新的接口才对?毕竟本来语文课代表类和体育课代表类各自实现了一个无用的方法。

按照第二种方案重构后的方法

抽象出来三种能力接口

//语文能力接口
public interface IGoodAtChinese {
    void canWrite();
}
//英语能力接口
public interface IGoodAtEnglish {
    void canEnglish();
}
//体育能力接口
public interface IGoodAtPhysicalEducation {
    void canRun();
}

班长、体育课代表、语文课代表分别实现

//班长类
public class SquadLeader implements IGoodAtChinese,IGoodAtEnglish,IGoodAtPhysicalEducation{
    @Override
    public void canRun() {
        System.out.println("我是班长,我能跑");
    }

    @Override
    public void canWrite() {
        System.out.println("我是班长,我能写");
    }

    @Override
    public void canEnglish() {
        System.out.println("我是班长,我英语好");
    }
}
//语文课代表类
public class ChineseClassRepresentative implements IGoodAtChinese{
    
    @Override
    public void canWrite() {
        System.out.println("我是语文课代表,我能写");
    }
}
//体育课代表类
public class PhysicalEducationClassRepresentative implements IGoodAtPhysicalEducation{
    @Override
    public void canRun() {
        System.out.println("我是体育课代表,我能跑");
    }
}

最后运行下

public class Main {

    public static void main(String[] args) {
        //班长发言
        SquadLeader squadLeader = new SquadLeader();
        squadLeader.canRun();
        squadLeader.canWrite();
        squadLeader.canEnglish();
        
        //语文课代表发言
        ChineseClassRepresentative chineseClassRepresentative = new ChineseClassRepresentative();
        chineseClassRepresentative.canWrite();
        
        //体育课代表发言
        PhysicalEducationClassRepresentative physicalEducationClassRepresentative = new PhysicalEducationClassRepresentative();
        physicalEducationClassRepresentative.canRun();
    }
}

运行结果如下

我是班长,我能跑
我是班长,我能写
我是班长,我英语好
我是语文课代表,我能写
我是体育课代表,我能跑

UML类图变成了如下所示

image-20240219171212854

职责明确,逻辑清晰,这是一次成功的改造~

实际上,这次的重构过程就是根据接口隔离原则做的一次重构

3、案例总结

接口隔离原则就是当我一个类通过接口依赖(使用)另一个类的时候,要保证依赖的该接口是最小的,接口里面有方法用不到的,就进行隔离,而隔离的做法就是,就对原来接口进行拆分,拆分为最小粒度,来避免耦合

三、和单一职责原则的关系

或许看到接口隔离原则这样的定义很多人会觉得和单一职责原则很像,但是这两个原则还是有着很鲜明的区别,接口隔离原则和单一职责原则的审视角度是不同的,单一职责原则要求类和接口职责单一,注重的是职责,是业务逻辑上的划分,而接口隔离原则要求方法要尽可能的少,是在接口设计上的考虑

根据接口隔离原则拆分接口时,首先必须满足单一职责原则

END.


#设计模式
目录