【面向对象设计原则】之接口隔离原则(ISP)
接口隔离原则(Interface Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
从接口隔离原则的定义可以看出,他似乎跟SRP有许多相似之处。 是的其实ISP和SRP都是强调职责的单一性, 接口隔离原则告诉我们在定义接口的时候要根据职责定义“较小”的接口,不要定义“高大全”的接口。也就是说接口要尽可能的职责单一,这样更容易复用,暴露给客户端的方法更具有“针对性”, 比如定义一个接口包括一堆访问数据库的方法, 有包括一堆访问网络的方法,还包括一些权限认证的方法。 把这么一摊子风牛马不相及的方法封装到一个接口里面,显然是不合适的, 如果客户程序只想用到数据访问的一些功能,但是调用接口的时候你把访问网络的方法和权限认证的方法暴露给客户,这使得客户程序感到“疑惑”,那么这个接口就不ISP,它很显然的构成了接口污染。
注意: 这里所说的接口是广义上的接口,他是一组契约, 是提供给程序交互的一组约定,并非各种语言interface 关键字定义的一组方法的结集合。但是这里所说的接口可以用各种语言的关键字interface 来定义,当然也可以用抽象类,类等等来定义。
假设有个客户提出了软件系统的需求:
1. 用户可以使用第三方QQ,微信,微博登录到系统。
2.系统中包括人员管理人员管理。
3.访问第三方的API获取一些数据。
好了拿到这个需求后首先经过分析,简单的原型设计,数据库设计之后开始编写代码了。 通常第一步定义接口。很快接口就定义出来了如下:
public interface IObject
{
void Connection(string connectionString);
SqlDataReader ExcuteSql(string sql);
string LoginWithQQ(string token);
string LoginWithWeibo(string token);
string LoginWithWeiXin(string token);
string GetDataFromAPI(string url, string token);
}
这个看起来还不错,接口已经定义了,写个具体类继承一下这个接口并实现所有的方法,现在就可以实现业务,写界面了。 等过了几天客户说 在给我加上支付宝登录。那好再加一个支付宝登录接口,代码现在长这样子:
public interface IObject
{
void Connection(string connectionString);
SqlDataReader ExcuteSql(string sql);
string LoginWithQQ(string token);
string LoginWithWeibo(string token);
string LoginWithWeiXin(string token);
string GetDataFromAPI(string url, string token);
string LoginWithAlipay(string token);
}
再在实现类中实现一下LoginWithAlipay方法 就好了。
时间在推移,一天客户说再给我加个百度登录,好吧套路有了加一个就是了,有啥了不起。 时间依旧。。。 客户说加个 facebook 登录, 。。。加个 Linkedin。。。, 尼玛 没完没了了, 现在接口已经变成这样子了:
public interface IObject
{
void Connection(string connectionString);
SqlDataReader ExcuteSql(string sql);
string LoginWithQQ(string token);
string LoginWithWeibo(string token);
string LoginWithWeiXin(string token);
string GetDataFromAPI(string url, string token);
string LoginWithAlipay(string token);
string LoginWithTwitter(string token);
string LoginWithFaceBook(string token);
string LoginWithRenRen(string token);
string LoginWithBaidu(string token);
string LoginWithDropbox(string token);
string LoginWithGithub(string token);
//这里省略10000字
stringLoginWithLinkedin(string token);
}
有一天这个接口自己都不想看了,太多方法了,更何况实现类中的代码都七八千行了。
于是决定重构, 现在回头看看这个接口早就应该重构了,甚至一开始定义的时候就应该拆分,接口的名字都不知道怎么命名(一般在写代码的时候类,接口,方法的名字不知道怎么命名的时候就是该重构的时候了)竟然起了IObject这么奇葩的名字,这个设计显然是烂到家了, 他几乎违背了我们讲过的所有设计原则, 必须到了要重构的时候了。
来吧,重构吧,经过分析第一步先根据功能来划分将IObject接口拆分成三个“小”接口:
1.数据库操作相关的抽取到一个接口中(IDatabaseProvider)。
2.第三方API调用相关的方法抽取到一个接口中(IThirdpartyAPIProvider)。
3.第三方登陆相关的方法抽取到一个接口中(IThirdpartyAuthenticationProvider)。
现在代码变成这个样子:
public interface IDatabaseProvider
{
SqlDataReader ExcuteSql(string sql);
string LoginWithQQ(string token);
} public interface IThirdpartyAPIProvider
{
string Get(string url, string token);
} public interface IThirdpartyAuthenticationProvider
{
string LoginWithQQ(string token);
string LoginWithWeibo(string token);
string LoginWithWeiXin(string token);
string LoginWithAlipay(string token);
string LoginWithTwitter(string token);
string LoginWithFaceBook(string token);
string LoginWithRenRen(string token);
string LoginWithBaidu(string token);
string LoginWithDropbox(string token);
string LoginWithGithub(string token);
//这里省略10000字
string LoginWithLinkedin(string token);
}
这下看起来好多了, 但是IThirdpartyAuthenticationProvider 代码还很多,还很丑陋,有没有办法再进一步重构呢? 答案是肯定。 第二步 我们可以将第三方登录的接口中的LogigWithxxx方法提到一个单独的接口中,其他具体站点的接口再继承这个接口,代码如下:
public interface IThirdpartyAuthenticationProvider
{
string Login(string token);
} public interface IQQAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface IWeiboAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface IWeiXinAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface IAlipayAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface ITwitterAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface IFaceBookAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface IRenRenAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface IBaiduAuthenticationProvider:IThirdpartyAuthenticationProvider{}
public interface IDropboxAuthenticationProvider : IThirdpartyAuthenticationProvider { }
public interface IGitHubAuthenticationProvider:IThirdpartyAuthenticationProvider{}
//这里省略10000字
public interface ILinkedinAuthenticationProvider : IThirdpartyAuthenticationProvider { }
这这下就好多了。 我们分析一下重构后的代码有什么好处:
1. 接口的职责更单一了,调用目标更清晰了,每一个接口就专门做一件事情。符合SRP了。
2. 在操作数据库的时候不会在IDatabase接口中调到其它的第三方API调用和第三方登录认证相关的方法,每一个几口更专注了。符合ISP了。
3.在添加新的第三方登录的时候不需要在修改原来的实现 类了,核心业务逻辑只需要加一个接口和接口的实现类就可以了。符合OCP了。
4. 提升了代码的稳定性,可维护性和可扩展性。
当然任何事情都具有两面性,如果将一件好事做到极端有可能就会走向反面, 比方说定义一个User实体的接口:
public interface IIdProperty { int Id { get; set; } }
public interface IFirstNameProperty { string FirstName { get; set; } }
public interface ILastNameProperty { string LastName { get; set; } }
public interface IAgeProperty { int Age { get; set; } }
public interface IBirthdayProperty { DateTime Birthday { get; set; } } public class User:IIdProperty,IFirstNameProperty,ILastNameProperty,IAgeProperty,IBirthdayProperty
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public DateTime Birthday { get; set; }
}
把每个属性都定义成一个接口那就不可取了,也是没有意义的,反而给维护或扩展带来不必要的麻烦,这就是使用接口时要注意的地方:
在使用接口时要注意控制接口的粒度,接口定义的粒度不能太细,也不能太粗。 接口粒度太细,系统中就会出现接口泛滥,接口和实现类急剧膨胀,反而不易维护;接口粒度太粗,就会违背ISP,系统的灵活性就会降低,不易维护和扩展。
关联阅读:
【面向对象设计原则】之原则概述
【面向对象设计原则】之单一职责原则(SRP)
【面向对象设计原则】之开闭原则(OCP)
【面向对象设计原则】之里氏替换原则(LSP)
【面向对象设计原则】之依赖倒置原则(DIP)
【面向对象设计原则】之接口隔离原则(ISP)的更多相关文章
- 面向对象的六大原则之 接口隔离原则——ISP
ISP = Interface Segregation Principle ISP的定义如下: 1.客户端不应该依赖他不需要的接口 2.一个类对另外一个类的依赖性应该是建立在最小的接口上 3.不应 ...
- 设计原则:接口隔离原则(ISP)
接口隔离原则的英文是Interface Segregation Principle,缩写就是ISP.与里氏替换原则一样其定义同样有两种 定义1: Clients should not be force ...
- C# 实例解释面向对象编程中的接口隔离原则
在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...
- IOS设计模式的六大设计原则之接口隔离原则(ISP,Interface Segregation Principle)
定义 客户端不应该依赖它不需要的接口: 一个类对另一个类的依赖应该建立在最小的接口上. 定义解读 定义包含三层含义: 一个类对另一个类的依赖应该建立在最小的接口上: 一个接口代表一个角色,不应该将不同 ...
- 最简单直接地理解Java软件设计原则之接口隔离原则
理论性知识 定义 接口隔离原则, Interface Segregation Principle,(ISP). 一个类对应一个类的依赖应该建立在最小的接口上: 建立单一接口,不要建立庞大臃肿的接口: ...
- 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:htt ...
- 设计模式 第一天 UML图,设计模式原则:开闭原则、依赖倒转原则、接口隔离原则、合成复用原则、迪米特法则,简单工厂模式
1 课程大纲 2 UML的概述 总结: UML unified model language 统一建模语言 一共有十种图: 类图 用例图 时序图 * 对象图 包图 组件图 部署图 协作图 状态图 (最 ...
- 设计模式学习--面向对象的5条设计原则之接口隔离原则--ISP
一.ISP简介(ISP--Interface Segregation Principle): 使用多个专门的接口比使用单一的总接口要好.一个类对另外一个类的依赖性应当是建立在最小的接口上的.一个接口代 ...
- S.O.L.I.D: PHP 面向对象设计的五个基准原则
S.O.L.I.D 是首个 5 个面向对象设计 (OOD) 准则的首字母缩写,这些准则是由 Robert C. Martin 提出的,他更为人所熟知的名字是 Uncle Bob. 这些准则使得开发出易 ...
- 面向对象设计(OOD)七大原则
这篇文章我会不停的维护它,它将会越来越长,但它是关于我在面向对象中的一些学习的思考心得.希望对自己对各位都能实用处. 开篇前,说明一下写这篇文章的原因.原因是由于设计模式.由于设计模式里的各种 ...
随机推荐
- 《Algorithms Unlocked》读书笔记1——循环和递归
<Algorithms Unlocked>是 <算法导论>的合著者之一 Thomas H. Cormen 写的一本算法基础. 书中没有涉及编程语言,直接用文字描述算法,我用 J ...
- python str.format()
python中的字符串格式函数str.format(): #使用str.format()函数 #使用'{}'占位符 print('I\'m {},{}'.format('Hongten','Welco ...
- elasticsearch5.3安装插件head
1.下载并配置nodejscd /usr/local/src/wget https://nodejs.org/dist/v6.9.5/node-v6.9.5-linux-x64.tar.xz & ...
- Android -- Annotation(注解)原理详解及常见框架应用
1,我们在上一篇讲到了EventBus源码及3.0版本的简单使用,知道了我们3.0版本是使用注解方式标记事件响应方法的,这里我们就有一个疑问了,为什么在一个方法加上类似于"@Subscrib ...
- 《Python自然语言处理》第二章 学习笔记
import nltk from nltk.book import * nltk.corpus.gutenberg.fileids() emma = nltk.corpus.gutenberg.wor ...
- Cookie中文乱码问题
页面一登录,页面二保存用户信息,放入Cookies里. 但是Cookies放入中文会引起编码问题,如报错“Control character in cookie value, consider BAS ...
- 类似智能购票的demo--进入页面后默认焦点在第一个输入框,输入内容、回车、right时焦点自动跳到下一个,当跳到select时,下拉选项自动弹出,并且可以按上下键选择,选择完成后再跳到下一个。
要实现的效果:进入页面后默认焦点在第一个输入框,输入内容.回车.right时焦点自动跳到下一个,当跳到select时,下拉选项自动弹出,并且可以按上下键选择,选择完成后再跳到下一个. PS:自己模拟的 ...
- Omi全新版本来袭 - 指令系统
写在前面 Omi框架到目前为止有三种版本. omi.js 使用 sodajs 为内置指令系统 omi.lite.js 不包含任何模板引擎 omi.mustache.js 使用 mustache.js为 ...
- Spring BeanFactoryPostProcessor
使用场景:当在配置文件中需要配置Bean(参数不同,class相同,id不同时)冗余的情况 继承 BeanFactoryPostProcessor 覆盖 postProcessBeanFactory( ...
- 【转载】c语言数据的左移右移
原文地址:http://www.cnblogs.com/myblesh/articles/2431806.html 由于在飞控程序中执行效率对程序的影响相当大,所以一个好的运算效率很重要.左移右移比单 ...