• 「设计模式」六大原则之四:接口隔离原则小结



    「设计模式」六大原则系列链接:
    「设计模式」六大原则之一:单一职责小结
    「设计模式」六大原则之二:开闭职责小结
    「设计模式」六大原则之三:里氏替换原则小结
    「设计模式」六大原则之四:接口隔离原则小结
    「设计模式」六大原则之五:依赖倒置原则小结
    「设计模式」六大原则之六:最小知识原则小结

    六大原则体现很多编程的底层逻辑:高内聚、低耦合、面向对象编程、面向接口编程、面向抽象编程,最终实现可读、可复用、可维护性。

    设计模式的六大原则有:

    • Single Responsibility Principle:单一职责原则
    • Open Closed Principle:开闭原则
    • Liskov Substitution Principle:里氏替换原则
    • Law of Demeter:迪米特法则(最少知道原则)
    • Interface Segregation Principle:接口隔离原则
    • Dependence Inversion Principle:依赖倒置原则
      把这六个原则的首字母联合起来( L 算做一个)就是 SOLID (solid,稳定的),其代表的含义就是这六个原则结合使用的好处:建立稳定、灵活、健壮的设计。

    本文介绍 SOLID 中的第四个原则:接口隔离原则。它对应 SOLID 中的英文字母“I”。

    1.接口隔离原则定义

    接口隔离原则的英文翻译是“ Interface Segregation Principle”,缩写为 ISP。

    Robert Martin 在 SOLID 原则中是这样定义它的:“Clients should not be forced to depend upon interfaces that they do not use。”直译成中文的话就是:客户端不应该被强迫依赖它不需要的接口。

    其中的“客户端”,可以理解为接口的调用者或者使用者。

    接口隔离的原则的目的是系统解开耦合,从而容易重构。

    接口尽量小,功能尽量单一,说白了就是接口粒度要细。

    2. 如何理解

    “接口”这个名词可以用在很多场合中。生活中我们可以用它来指插座接口等。在软件开发中,我们既可以把它看作一组抽象的约定,也可以具体指系统与系统之间的 API 接口,还可以特指面向对象编程语言中的接口等。

    理解接口隔离原则的关键,就是理解其中的“接口”二字。在这条原则中,我们可以把“接口”理解为下面三种东西:

    • 一组 API 接口集合
    • 单个 API 接口或函数
    • OOP 中的接口概念

    如果把“接口”理解为一组接口集合,可以是某个微服务的接口,也可以是某个类库的接口等。如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。

    如果把“接口”理解为单个 API 接口或函数,部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数。

    如果把“接口”理解为 OOP 中的接口,也可以理解为面向对象编程语言中的接口语法。那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。

    在具体应用该原则时,可以参考下面几个规则衡量:

    • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
    • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
    • 根据环境和业务不同的标准去考虑。
    • 提高内聚,减少对外交互。让接口用最少的方法完成最多的事情。

    分享一张来自 java硕哥 整理的思维导图:
    在这里插入图片描述

    3. 接口隔离原则与单一职责原则的区别

    单一职责原则针对的是模块、类、接口的设计。

    接口隔离原则相对于单一职责原则,一方面更侧重于接口的设计,另一方面它的思考角度也是不同的。

    接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。

    如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。

    2. 举例说明

    实现一个音乐播放器, 定义一个接口:

    interface IMusicPlayer {
        //开始
        void start(); 
        //停止
        void stop();
        //暂停
        void pause();
        //复原
        void resume();
        //获取歌曲时长
        String getSongLength();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里满足了单一职责原则,却不满足接口隔离。

    假如我们现在有个歌曲展示器 SongDisplayer,需要展示歌曲时长,那么我们也应该有个 getSongLength() 函数,我们直接实现 IMusicPlayer接口吗,实现这个接口就必须实现里面的start()等方法,但是这些方法肯定不是我需要的,也不是我应该有的,这就是问题,因为接口不够小,不干净,不纯粹,明显违背了接口隔离原则,我们就可以对接口进行拆分:

    //音乐播放器就仅限于对播放的控制
    interface IMusicPlayer {
        //开始
        void start(); 
        //停止
        void stop();
        //暂停
        void pause();
        //复原
        void resume();
        ...
    }
    
    //歌曲展示器就仅限于对歌曲信息的展示
    interface ISongDisplayer {
        //获取歌曲时长
        String getSongLength();
        //获取歌曲名字
        String getSongName();
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    当我们实现播放器时 可以同时实现这两个接口即可,只需要展示歌曲信息的话,只需要单独实现 ISongDisplayer即可。

    public class MyPlayer implements IMusicPlayer,ISongDisplayer{
      //……
    }
    
    • 1
    • 2
    • 3

    这样实现便更利于维护,也符合我们的接口隔离原则。

    4. 小结

    接口要尽量小,尽量单一,无关的接口不要冗余。粒度小,更耦合,更灵活,不会伤筋动骨。

    参考:

    《设计模式之美》

    《重学 Java 设计模式》

    《Android 源码设计模式解析与实战》

    设计模式之基-六大设计原则

  • 相关阅读:
    AI图书推荐:用ChatGPT按需DIY定制来赚钱
    快速解决服务器被ddos恶意攻击和高防IP试用那些行业
    zookeeper最基础教程
    easyx库的学习(鼠标信息)
    Spring AOP
    搜索与图论-树与图的广度优先遍历
    公众号查题:一步一步搭建自己的查题公众号(内含永久接口)
    Promise封装Ajax请求
    回归测试套规划
    游戏热更新进修——Lua编程
  • 原文地址:https://blog.csdn.net/jun5753/article/details/126877957