【设计模式】七、面向对象设计原则之接口隔离原则

71 阅读5分钟

这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情

系列文章|源码

github.com/tyronczt/de…

定义-是什么

接口隔离原则(Interface Segregation Principle, ISP)的含义:

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

  • 客户端不应该依赖它不用的接口;它需要什么接口就提供什么接口,把不需要的接口剔除掉。 => 低耦合
    • 如果客户依赖了不需要的接口,就要面临不需要的接口变动带来的风险
    • 提倡客户不应被迫使用对其无用的方法或功能

The dependency of one class to another one should depend on the smallest possible interface.

  • 类间的依赖关系应建立在最小的接口上。 => 高内聚
    • 把没有关系的接口合并在一起,会形成一个臃肿的大接口,这是对职责分配和接口的污染
    • 最小接口:满足项目需求的相似功能
    • 把庞大臃肿的接口拆分成更小和更具体的接口

接口隔离:多个专门的接口 > 单一的总接口

思考-为什么

接口隔离原则符合我们常说的高内聚、低耦合的设计思想,可以使类具有很好的可读性、可扩展性和可维护性。我们在设计接口的时候,要多花时间去思考,要考虑业务模型,包括对以后有可能发生变更的地方还要做一些预判。所以,对于抽象、对于业务模型的理解是非常重要的。

接口隔离原则和单一职责原则

从功能上来看,接口隔离原则和单一职责原则都是为了提高类的内聚, 降低类之间的耦合, 体现了封装的思想。但二者还是有区别的。

(1)从原则约束来看: 接口隔离原则更关注的是接口依赖程度的隔离;而单一职责原则更加注重的是接口职责的划分。

(2)从接口的细化程度来看: 单一职责原则对接口的划分更加精细,而接口隔离原则注重的是相同功能的接口的隔离。接口隔离里面的最小接口有时可以是多个单一职责的公共接口。

(3)单一职责原则更加偏向对业务的约束;接口隔离原则更加偏向设计架构的约束。这个应该好理解,职责是根据业务功能来划分的,所以单一原则更加偏向业务;而接口隔离更多是为了“高内聚”,偏向架构的设计。

接口隔离原则的优点

接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。

  1. 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  2. 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  3. 如果接口的粒度大小定义合理,能够保证系统的稳定性;然而,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  4. 使用多个专门的接口能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  5. 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

接口隔离原则的实现方法

在具体应用接口隔离原则时,应该根据以下几个规则来衡量。

  1. 接口要尽量小 不能出现Fat Interface;但是要有限度,首先不能违反单一职责原则(不能一个接口对应半个职责)。
  2. 接口要高内聚 在接口中尽量少公布public方法。 接口是对外的承诺,承诺越少对系统的开发越有利。
  3. 定制服务 只提供访问者需要的方法。例如,为管理员提供IComplexSearcher接口,为公网提供ISimpleSearcher接口。
  4. 接口的设计是有限度的 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同, 需要深入了解业务逻辑。

接口隔离原则的建议

  1. 一个接口只服务于一个子模块或业务逻辑;
  2. 通过业务逻辑压缩接口中的public方法;
  3. 已被污染了的接口,尽量去修改;若变更的风险较大,则采用适配器模式转化处理;
  4. 拒绝盲从;

应用-怎么用

案例:动物行为

// 动物行为
public interface IAnimalAction {
    void eat(); // 吃

    void fly(); // 飞

    void swim(); // 游泳
}

// 狗
public class Dog implements IAnimalAction {
    @Override
    public void eat() {

    }

    @Override
    public void fly() {
      // 狗不会飞
    }

    @Override
    public void swim() {

    }
}

对于狗来说,其他两种行为都ok,但是不会飞。假如再增加一种鸟类动物大雁,大雁不会游泳。

那么使用接口隔离原则来实现的一种可能:将接口拆分

public interface IEatAnimalAction {
    void eat();
}

public interface IFlyAnimalAction {
    void fly();
}

public interface ISwimAnimalAction {
    void swim();
}

public class Bird implements IFlyAnimalAction, IEatAnimalAction {
    @Override
    public void eat() {

    }

    @Override
    public void fly() {

    }
}

设计粒度的注意事项

  • 设计粒度上太小会适得其反

  • 设计粒度参考,一个接口只服务于一个子模块或者业务逻辑

接口隔离原则 vs 单一职责原则

  • 约束偏向:接口隔离偏向约束架构设计,单一职责偏向约束业务

  • 细化程度:单一职责更精细,接口隔离注重相似接口的隔离

参考

设计模式六大原则(四)----接口隔离原则 - 掘金

接口隔离原则 | MRCODE-BOOK

口隔离原则是什么? 单一职责原则的好兄弟【设计模式系列7】_哔哩哔哩_bilibili