05.接口隔离原则介绍

news/2025/3/6 19:44:01/文章来源:https://www.cnblogs.com/yc211/p/18756268

05.接口隔离原则介绍

目录介绍

  • 01.问题思考的分析
  • 02.学习接口隔离目标
  • 03.理解接口隔离原则
  • 04.接口隔离原则思想
  • 05.一组API接口集合案例
  • 06.单个API接口或函数
  • 07.总结接口隔离原则
  • 08.思考一道课后题
  • 09.接口隔离原则总结

推荐一个好玩网站

一个最纯粹的技术分享网站,打造精品技术编程专栏!编程进阶网

https://yccoding.com/

设计模式Git项目地址:https://github.com/yangchong211/YCDesignBlog

简介: 接口隔离原则(ISP)是SOLID原则之一,强调客户端不应依赖于它们不需要的接口。通过将庞大而臃肿的接口拆分为更小、更具体的接口,确保每个接口只包含客户端真正需要的方法,从而提高代码的可维护性和灵活性。本文详细介绍了接口隔离原则的概念、核心思想、实现方式及案例分析,并对比了其与单一职责原则的区别。关键点包括:接口应精简、独立且可扩展,避免强迫实现不必要的方法,减少系统的耦合性。

01.问题思考的分析

什么叫作接口隔离法则,它和面向对象中的接口有何区别?在哪些场景需要注意接口隔离原则?

02.学习接口隔离目标

学习了 SOLID 原则中的单一职责原则、开闭原则和里式替换原则,今天我们学习接口隔离原则。它对应 SOLID 中的英文字母“I”。

对于这个原则,最关键就是理解其中“接口”的含义。那针对“接口”,不同的理解方式,对应在原则上也有不同的解读方式。

除此之外,接口隔离原则跟我们之前讲到的单一职责原则还有点儿类似,所以今天我也会具体讲一下它们之间的区别和联系。

03.理解接口隔离原则

接口隔离原则的英文翻译是“ Interface Segregation Principle”,缩写为 ISP。 Robert Martin 在 SOLID 原则中是这样定义它的:“Clients should not be forced to depend upon interfaces that they do not use。”

直译成中文的话就是:客户端不应该强迫依赖它不需要的接口。其中的“客户端”,可以理解为接口的调用者或者使用者。

简单来说,这个原则鼓励将庞大而臃肿的接口拆分为更小、更具体的接口,以便客户端只需依赖它们所需的接口

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

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

  1. 一组 API 接口集合
  2. 单个 API 接口或函数
  3. OOP 中的接口概念

这些接口定义了类或模块与外部世界的交互方式。接口可以是抽象类、接口(interface)或者具体类中的公共方法。它们定义了类或模块的行为和功能,供其他类或模块进行调用和使用。

04.接口隔离原则思想

4.1 先说下遇到的问题

在早期的软件开发中,常常使用大而全的接口来定义类之间的交互方式。这些接口通常包含了各种方法,无论是否被实际使用。这种设计方式存在以下问题:

  1. 接口臃肿:大而全的接口会包含很多方法,其中有些方法对于某些类或模块来说是不必要的,导致接口冗余和庞大。
  2. 强迫实现:当一个类实现一个接口时,它必须实现接口中的所有方法,即使某些方法对于该类来说是无意义的。这导致了冗余的实现代码。
  3. 脆弱性:当接口发生变化时,所有实现该接口的类都需要进行相应的修改,即使这些变化对于某些类来说是不相关的。这增加了系统的耦合性和维护成本。

4.2 设计这原则的思想

接口隔离原则的核心思想是:

  1. 接口要小而专一:接口不应该包含太多功能,应该拆分成多个小接口,每个接口只定义客户端需要的方法。
  2. 避免臃肿接口:如果接口定义了过多的方法,某些客户端可能只需要其中一部分功能,这会导致不必要的依赖,增加代码的复杂性。

理解接口隔离原则的关键点如下,用更加通俗的话来理解:

  1. 接口应该精简:接口应该只包含客户端所需的方法,而不应该强迫客户端实现它们不需要的方法。这样可以避免客户端因为实现不需要的方法而产生冗余代码。
  2. 接口应该独立:接口应该独立于具体的实现细节,以便在不影响客户端的情况下进行修改和扩展。这样可以提高系统的灵活性和可维护性。
  3. 接口应该可扩展:接口应该容易扩展,以便在需要添加新功能时能够方便地进行修改。这样可以避免对现有接口进行破坏性的修改。

通俗地说,一个接口只应该包含客户端真正需要的功能,其他无关的功能应该分离到不同的接口中。

4.3 实现接口隔离原则

  1. 拆分大接口:将大而全的接口拆分为更小、更具体的接口,每个接口只包含相关的方法。这样可以避免类实现不需要的方法,减少接口的冗余和庞大。
  2. 定义细化接口:根据不同的使用场景,定义细化的接口,以满足各个类或模块的特定需求。每个类只需实现其所需的接口,避免了强迫实现不相关的方法。
  3. 接口继承:通过接口继承,将通用的方法定义在父接口中,然后派生出更具体的子接口。
  4. 接口组合:使用接口组合的方式,将多个小接口组合成一个更大的接口。这样,类可以根据需要选择性地实现组合接口中的方法,避免了实现不需要的方法。
  5. 依赖注入:通过依赖注入的方式,将类所依赖的接口作为参数传递给类的构造函数或方法。这样,类只需依赖其所需的接口,而不需要依赖不相关的接口。

05.一组API接口集合案例

5.1 错误示范案例

假设我们在电商系统中设计了一个用户操作接口UserOperations,包含了用户的所有操作:

interface UserOperations {void createOrder();void cancelOrder();void browseProducts();void manageAccount();void applyDiscount();
}

在这个设计中,所有的用户操作都集中在一个接口中,但并不是所有用户都需要所有这些操作。

比如,对于普通用户,可能只需要浏览商品、创建订单和管理账户的功能,而对于管理员用户,则可能更关注于应用折扣和订单管理的功能。

这种设计违反了接口隔离原则,导致客户端依赖了很多不需要的方法,增加了系统的复杂性和维护成本。

5.2 正确示范案例

为了遵循接口隔离原则,我们可以将UserOperations接口拆分成多个更小、更专一的接口:

 interface OrderOperations {void createOrder();void cancelOrder();
}interface ProductOperations {void browseProducts();
}interface AccountOperations {void manageAccount();
}interface AdminOperations {void applyDiscount();
}

然后根据不同的用户类型,实现各自所需的接口:

class NormalUser implements OrderOperations, ProductOperations, AccountOperations {@Overridepublic void createOrder() {// 创建订单逻辑}@Overridepublic void cancelOrder() {// 取消订单逻辑}@Overridepublic void browseProducts() {// 浏览商品逻辑}@Overridepublic void manageAccount() {// 管理账户逻辑}
}class AdminUser implements AdminOperations, OrderOperations {@Overridepublic void applyDiscount() {// 应用折扣逻辑}@Overridepublic void createOrder() {// 创建订单逻辑}@Overridepublic void cancelOrder() {// 取消订单逻辑}
}

这种设计方式将接口职责进行了合理的划分,使得每个接口只包含客户端真正需要的方法,符合接口隔离原则的要求。

在电商系统中,遵循接口隔离原则可以确保用户操作接口不会变得臃肿,每个用户类型只依赖于它们需要的功能接口,从而避免了不必要的依赖和实现负担。

5.3 电商案例总结

问题:接口臃肿

在电商系统中,如果我们为用户设计了一个包含所有操作的接口,会导致接口臃肿,增加了实现类的复杂性。

客户端在实现时必须实现所有方法,即使它们中的某些方法在当前场景中不需要使用。

解决方式:将大接口拆分成多个小接口,每个接口只包含相关的方法。这样可以提高代码的可维护性和灵活性,减少不必要的依赖。

06.单个API接口或函数

现在我们再换一种理解方式,把接口理解为单个接口或函数(以下为了方便讲解,我都简称为“函数”)。

那接口隔离原则就可以理解为:函数的设计要功能单一,不要将多个不同的功能逻辑在一个函数中实现。接下来,我们还是通过一个例子来解释一下。

public class Statistics {private Long max;private Long min;private Long average;private Long sum;private Long percentile99;private Long percentile999;//...省略constructor/getter/setter等方法...
}public Statistics count(Collection<Long> dataSet) {Statistics statistics = new Statistics();//...省略计算逻辑...return statistics;
}

在上面的代码中,count() 函数的功能不够单一,包含很多不同的统计功能,比如,求最大值、最小值、平均值等等。

按照接口隔离原则,我们应该把 count() 函数拆成几个更小粒度的函数,每个函数负责一个独立的统计功能。拆分之后的代码如下所示:

public Long max(Collection<Long> dataSet) { //... }
public Long min(Collection<Long> dataSet) { //... } 
public Long average(Colletion<Long> dataSet) { //... }
// ...省略其他统计函数...

不过,你可能会说,在某种意义上讲,count() 函数也不能算是职责不够单一,毕竟它做的事情只跟统计相关。

在讲单一职责原则的时候,也提到过类似的问题。实际上,判定功能是否单一,除了很强的主观性,还需要结合具体的场景。

如果在项目中,对每个统计需求,Statistics 定义的那几个统计信息都有涉及,那 count() 函数的设计就是合理的。相反,如果每个统计需求只涉及 Statistics 罗列的统计信息中一部分,比如,有的只需要用到 max、min、average 这三类统计信息,有的只需要用到 average、sum。而 count()函数每次都会把所有的统计信息计算一遍,就会做很多无用功,势必影响代码的性能,特别是在需要统计的数据量很大的时候。所以,在这个应用场景下,count()函数的设计就有点不合理了,我们应该按照第二种设计思路,将其拆分成粒度更细的多个统计函数。

接口隔离原则跟单一职责原则有点类似,不过稍微还是有点区别。单一职责原则针对的是模块、类、接口的设计。

接口隔离原则相对于单一职责原则,一方面它更侧重于接口的设计,另一方面它的思考的角度不同。它提供了一种判断接口是否职责单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。

07.总结接口隔离原则

如何理解“接口隔离原则”? 理解“接口隔离原则”的重点是理解其中的“接口”二字。这里有三种不同的理解。

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

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

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

接口隔离原则与单一职责原则的比较,他们各自的特点:

  1. 单一职责原则针对的是模块、类、接口的设计。接口隔离原则相对于单一职责原则,一方面更侧重于接口的设计,另一方面它的思考角度也是不同的。
  2. 接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。

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

  1. 关注点不同:单一职责原则关注的是类或模块的职责,它强调将不同的职责分离开来,使得类或模块的设计更加清晰和可维护。而接口隔离原则关注的是接口的设计,即将大而全的接口拆分为小而精简的接口,以避免类实现不需要的方法,减少接口的冗余和庞大。
  2. 范围不同:单一职责原则是针对类或模块级别的设计原则,它要求一个类或模块只负责一项职责。而接口隔离原则是针对接口级别的设计原则,它要求将接口细化为只包含相关方法的小接口,以满足不同类或模块的特定需求。
  3. 目的不同:单一职责原则的目的是提高类或模块的内聚性,使其职责清晰、单一,易于理解和维护。而接口隔离原则的目的是提高系统的灵活性、可维护性和可扩展性,通过拆分接口,避免类实现不需要的方法,减少接口的冗余和庞大。

08.思考一道课后题

java.util.concurrent 并发包提供了 AtomicInteger 这样一个原子类,其中有一个函数 getAndIncrement()是这样定义的:给整数增加一,并且返回未増之前的值。

我的问题是,这个函数的设计是否符合单一职责原则和接口隔离原则?为什么?

/*** Atomically increments by one the current value.* @return the previous value*/
public final int getAndIncrement() {//...}

09.接口隔离原则总结

  1. 接口隔离问题思考:什么叫作接口隔离法则,它和面向对象中的接口有何区别?在哪些场景需要注意接口隔离原则?
  2. 如何理解接口隔离原则:简单来说,这个原则鼓励将庞大而臃肿的接口拆分为更小、更具体的接口,以便客户端只需依赖它们所需的接口。
  3. 接口隔离原则的核心思想:1.接口应该精简;2.接口应该独立;3.接口应该可以可拓展。
  4. 如何理解这个原则中的接口:可以把它看作一组抽象的约定,也可以指系统与系统之间的API接口,还可以特指面向对象编程语言中的接口等。
  5. 接口隔离原则的背景:常常使用大而全的接口来定义类之间的交互方式,会导致接口臃肿,接口新增方法强迫子类都实现,脆弱性。为了解决这些问题才产生这个原则。
  6. 实现接口隔离原则的方式:1.拆分大接口;2.定义细化接口;3.接口继承;4.接口组合;5.依赖注入
  7. 接口隔离原则的案例教学:看博客中的代码案例
  8. 接口隔离原则的优点:接口隔离原则的优点包括精简接口、避免强迫实现、提高灵活性、降低耦合性和提高可复用性。
  9. 接口隔离原则的缺点:可能导致接口数量增多,接口增多可能带来代码复杂,可能引入间接性的依赖关系。
  10. 接口隔离原则与单一职责原则的区别:主要区别有,他们关注点不同,使用范围不同,还有目的不同。

10.更多内容推荐

模块 描述 备注
GitHub 多个YC系列开源项目,包含Android组件库,以及多个案例 GitHub
博客汇总 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 YCBlogs
设计模式 六大设计原则,23种设计模式,设计模式案例,面向对象思想 设计模式
Java进阶 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM Java高级
网络协议 网络实际案例,网络原理和分层,Https,网络请求,故障排查 网络协议
计算机原理 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 计算机基础
学习C编程 C语言入门级别系统全面的学习教程,学习三到四个综合案例 C编程
C++编程 C++语言入门级别系统全面的教学教程,并发编程,核心原理 C++编程
算法实践 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 Leetcode
Android 基础入门,开源库解读,性能优化,Framework,方案设计 Android

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/894775.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推荐4本专著《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》书,非常感谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

SAS 9.4软件下载与安装教程

1、安装包 扫描下方二维码关注「软知社」,后台回复【043】三位数字即可免费获取分享链接,无广告拒绝套路;2、安装教程双击setup.exe安装,弹窗安装对话框简体中文,点击确定默认选择,点击下一步指定SAS安装主目录,选择C盘之外磁盘,点击下一步选择第二个,安装SAS Foundat…

MyBatis与其使用方法讲解

ORM在讲解Mybatis之前,我们需了解一个概念ORM(Object-Relational Mapping)对象关系映射,其是数据库与Java对象进行映射的一个技术.通过使用ORM,我们可以不用编写负责的Sql语句,而是通过操作对象来实现增删改查操作缺优分析优点提高开发效率,减少代码的重复性和维护成本 增加代码…

系统流程图

1.图书馆借阅管理系统流程图: 背景说明:在学校图书馆借阅管理系统中,学生借阅图书需要经过一系列流程。首先,学生携带校园卡前往借阅处,工作人员通过刷卡设备读取学生信息,系统验证学生身份是否有效。若身份无效,系统提示原因(如校园卡过期、欠费等)。若身份有效,学生…

基于Microsoft.Extensions.AI核心库实现RAG应用

本文介绍了如何基于Microsoft.Extensions.AI + Microsoft.Extensions.VectorData 一步一步地实现一个RAG(检索增强生成)应用,相信会对你有所帮助。如果你也是.NET程序员希望参与AI应用的开发,那就快快了解和使用基于Microsoft.Extensioins.AI + Microsoft.Extensions.Vecto…

Linux shell su command All In One

Linux shell su command All In One su !== sudo substitute user => su, 替换用户/切换用户 superuser do => sudo, 执行超级管理员用户权限 su - run a command with substitute user and group ID su - 使用替代用户和组 ID 来运行命令Linux shell su command All In …