草庐IT

设计原则之【接口隔离原则】

Gopher大威 2023-03-28 原文

设计原则是指导我们代码设计的一些经验总结,也就是“心法”;面向对象就是我们的“武器”;设计模式就是“招式”。

以心法为基础,以武器运用招式应对复杂的编程问题。

来吧,通过生活中一个小场景,一起系统学习这6大设计原则。

SOLID原则--SRP单一职责原则

SOLID原则--OCP开放封闭原则

SOLID法则--LSP里式替换原则

SOLID原则--ISP接口隔离原则

SOLID原则--DIP依赖反转原则

LOD迪米特法则

和表妹去逛超市...

表妹:哥啊,我想买酸奶?

我:买,问题不大。

表妹:他家的酸奶都在搞活动,买酸奶送鸡蛋。

我:那不挺好嘛?把鸡蛋也一起买啦。

表妹:可是我家还有好多鸡蛋......


你看,这种“捆绑式消费”,不就类似违反了我们软件设计中的“接口隔离原则”嘛?鸡蛋并不是我们想要的,但是它却跟我们想要的酸奶绑在一起,酸奶就变得“臃肿”了。

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

如何理解“接口”?

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

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

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

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

那么,接下来,我们从面向对象编程语言中的接口语法的角度来学习一下“接口隔离原则”。

我们一起来看“美女”,一般来说,长得好看的,身材不错的,气质出众的都可以成为美女。

所以,我们定义了一个IPettyGirl接口,显示美女的这几个特征。PettyGirl是该接口的实现类。

 1 // 美女接口
 2 public interface IPettyGirl {
 3     // 要有姣好的面孔
 4     public void goodLooking();
 5     // 要有好身材
 6     public void niceFigure();
 7     // 要有气质
 8     public void greatTemperament();
 9 }
10 11 // 接口的实现类
12 public class PettyGirl implements IPettyGirl {
13     private String name;
14 15     public PettyGirl(String  name){
16        this.name = name;
17     }
18 19     // 脸蛋漂亮
20     public void goodLooking() {
21          System.out.println(this.name + "---脸蛋很漂亮!");
22     }
23 24     // 气质要好
25     public void greatTemperament() {
26           System.out.println(this.name + "---气质非常好!");
27     }
28 29     // 身材要好
30     public void niceFigure() {
31           System.out.println(this.name + "---身材非常棒!");
32     }
33 }

我们仔细想一下,长得好看,拥有好的身材,而且气质还好的,当然是“美女”,甚至还是“女神”, “神仙小姐姐”,因为简直是无可挑剔。这是所有人心目中的评判标准。

但是,我们每个人都会有自己不同的审美标准。假如,隔壁老王就认为“美女”应该是具有好看的皮囊,长得好看,身材好。老李就喜欢有趣的灵魂,认为气质好的才是“美女”。

那么,这样看来,IPettyGirl接口的设计就显得比较臃肿了,因为太宽泛了,其实每个人的审美标准都不同。所以,我们将该接口进行拆分,如下:

 1 // IGoodBodyGirl 好看的皮囊
 2 public interface IGoodBodyGirl {
 3      // 要有姣好的面孔
 4      public void goodLooking();
 5      // 要有好身材
 6      public void niceFigure();
 7 }
 8  9 // IGreatTemperamentGirl 有趣的灵魂
10 public interface IGreatTemperamentGirl {
11      // 要有气质
12      public void greatTemperament();
13 }
14 15 // PettyGirl1 老王心目中的美女---长得好看,身材好
16 public class PettyGirl1 implements IGoodBodyGirl {
17      private String name;
18 19      public PettyGirl1(String _name){
20         this.name = _name;
21   }
22 23      // 脸蛋漂亮
24      public void goodLooking() {
25         System.out.println(this.name + "---脸蛋很漂亮!");
26      }
27 28      // 身材要好
29      public void niceFigure() {
30         System.out.println(this.name + "---身材非常棒!");
31      }
32 }
33 34 // PettyGirl2 老李心目中的美女---气质好
35 public class PettyGirl2 implements IGreatTemperamentGirl {
36      private String name;
37      public PettyGirl2(String _name) {
38         this.name = _name;
39      }
40     
41      // 气质要好
42      public void greatTemperament() {
43         System.out.println(this.name + "---气质非常好!");
44      }
45 }

你看,这样的设计,是不是更加灵活,易扩展了?

假如,有些人认为心灵美的人,才叫“美女”。我们只需扩展如下代码,而无需改动之前的代码。

 1 // IMindBeautyGirl 心灵美
 2 public interface IMindBeautyGirl {
 3     // 心灵美
 4     public void mindbeauty();
 5 }
 6  7 // PettyGirl3 心灵美的美女
 8 public class PettyGirl3 implements IMindBeautyGirl {
 9     private String name;
10     public PettyGirl3(String _name) {
11         this.name = _name;
12     }
13     
14     // 心灵美
15     public void mindbeauty() {
16         System.out.println(this.name + "---心灵美!");
17     }
18 }

但是,如果按照IPettyGirl这个接口设计的话,我们不但要改动IPettyGirl这个接口,还要改每一个实现类。改不好,可能还会引入新的bug。

你看,接口粒度小,设计的改动也比较少。

接口是我们设计时对外提供的契约,通过分散定义多个接口,可以预防未来变更的扩散,提供系统的灵活性和可维护性。

不过,你可能发现,接口隔离原则跟单一职责原则有点类似。但是,还是有点区别的。

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

单一职责原则针对的是模块、类、接口的设计,注重的是职责,这是业务逻辑上的划分。

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

接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那么接口的设计就比较“臃肿”,违背了“接口隔离原则”。

那可能有同学会问:“如果一个接口,按照接口隔离原则要拆,但是按照单一职责原则就不用拆,那应该怎么办呢?”

其实很好办,根据接口隔离原则拆分时,首先必须满足单一职责原则。

总结

一个接口只干一件事,接口要精简单一。

目的:高内聚、低耦合。

好啦,每个设计原则是否运用得当,需要根据具体的业务场景,具体分析。

参考

极客时间专栏《设计模式之美》

《设计模式之禅》

 

有关设计原则之【接口隔离原则】的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  4. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  5. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  6. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  7. ruby - 最佳原则中的原则 - 2

    我似乎经常遇到一些设计问题,但我不知道是什么是真的很合适。一方面我经常听到我应该限制耦合和坚持单一职责,但当我这样做时,我常常发现它很困难到在需要时将信息获取到程序的一部分。为了例如,classSingerdefinitialize(name)@name=nameendattr:nameend那么Song应该是:classSongdefnew(singer)@singer=singerendend或classSongdefnew(singer_name)@singer_name=singer_nameendend后者耦合性小,按道理应该用。但如果我以后发现宋有什么需要了解更多歌手,我的

  8. ruby-on-rails - 设计通过 reset_password_token 获取用户 - 2

    我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow

  9. ruby-on-rails - Rails 5,公寓和设计 : sign in with subdomains are not working - 2

    我已经使用Apartment设置了一个Rails5应用程序(1.2.0)和Devise(4.2.0)。由于某些DDNS问题,应用只能在app.myapp.com下访问(请注意子域app)。myapp.com重定向到app.myapp.com。我的用例是每个注册该应用的用户(租户)都应该通过他们的子域(例如tenant.myapp.com)访问他们的特定数据。用户不应限定在其子域内。基本上应该可以从任何子域登录。重定向到租户的正确子域由ApplicationController处理。根据Devise标准,登录页面位于app.myapp.com/users/sign_in。这就是问题开始的

  10. ruby-on-rails - 设计中的 ArgumentError::RegistrationsController#new 错误的参数数量(2 代表 0..1) - 2

    我在关注RyanbatesRailsCast的devise和omniauth(第235集-devise-and-omniauth-revised)。当我尝试使用Twitter登录时,标题中不断出现错误。defself.new_with_session(params,session)ifsession["devise.user_attributes"]new(session["devise.user_attributes"],without_protection:true)do|user|user.attributes=paramsuser.valid?end完整跟踪:C:/Ruby20

随机推荐