接口隔离原则(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)的更多相关文章

  1. 面向对象的六大原则之 接口隔离原则——ISP

    ISP = Interface Segregation Principle   ISP的定义如下: 1.客户端不应该依赖他不需要的接口 2.一个类对另外一个类的依赖性应该是建立在最小的接口上 3.不应 ...

  2. 设计原则:接口隔离原则(ISP)

    接口隔离原则的英文是Interface Segregation Principle,缩写就是ISP.与里氏替换原则一样其定义同样有两种 定义1: Clients should not be force ...

  3. C# 实例解释面向对象编程中的接口隔离原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

  4. IOS设计模式的六大设计原则之接口隔离原则(ISP,Interface Segregation Principle)

    定义 客户端不应该依赖它不需要的接口: 一个类对另一个类的依赖应该建立在最小的接口上. 定义解读 定义包含三层含义: 一个类对另一个类的依赖应该建立在最小的接口上: 一个接口代表一个角色,不应该将不同 ...

  5. 最简单直接地理解Java软件设计原则之接口隔离原则

    理论性知识 定义 接口隔离原则, Interface Segregation Principle,(ISP). 一个类对应一个类的依赖应该建立在最小的接口上: 建立单一接口,不要建立庞大臃肿的接口: ...

  6. 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:htt ...

  7. 设计模式 第一天 UML图,设计模式原则:开闭原则、依赖倒转原则、接口隔离原则、合成复用原则、迪米特法则,简单工厂模式

    1 课程大纲 2 UML的概述 总结: UML unified model language 统一建模语言 一共有十种图: 类图 用例图 时序图 * 对象图 包图 组件图 部署图 协作图 状态图 (最 ...

  8. 设计模式学习--面向对象的5条设计原则之接口隔离原则--ISP

    一.ISP简介(ISP--Interface Segregation Principle): 使用多个专门的接口比使用单一的总接口要好.一个类对另外一个类的依赖性应当是建立在最小的接口上的.一个接口代 ...

  9. S.O.L.I.D: PHP 面向对象设计的五个基准原则

    S.O.L.I.D 是首个 5 个面向对象设计 (OOD) 准则的首字母缩写,这些准则是由 Robert C. Martin 提出的,他更为人所熟知的名字是 Uncle Bob. 这些准则使得开发出易 ...

  10. 面向对象设计(OOD)七大原则

    这篇文章我会不停的维护它,它将会越来越长,但它是关于我在面向对象中的一些学习的思考心得.希望对自己对各位都能实用处.     开篇前,说明一下写这篇文章的原因.原因是由于设计模式.由于设计模式里的各种 ...

随机推荐

  1. 结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!

    在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection.但是没有细致的讲到如何绑定,以及为什么这么 ...

  2. Java程序初始化的顺序

    Java程序初始化的顺序 java程序初始化工作可以在许多不同的代码块中来完成(例如:静态代码块.构造函数等),他们执行的顺序如下: 父类静态变量 父类静态代码块 子类静态变量 子类静态代码块 父类非 ...

  3. 【树莓派】修改树莓派盒子MAC地址

    用树莓派盒子,在某些客户方实施过程中,不同客户的网络环境对树莓派盒子的要求不同,网络管理配置要求MAC地址和IP绑定. 一种情况下,查询盒子的MAC地址,添加到网络管理的路由规则中即可: 另一种情况下 ...

  4. lua 数据类型

    lua 数据类型 8 种数据类型 类型 说明 nil 空类型 boolean 布尔类型 number 数值型, 浮点型 string 字符串 function 函数 userdata 用户自定义结构 ...

  5. ionic打包项目,运行时报错A problem occurred configuring root project 'android'。。。

    运行报错的原因是sdk没有下载完整 解决办法: 1,打开sdk manage.分别下载android support repository.Google play services.google re ...

  6. iOS 2017年, 上传审核被拒绝.到奔溃

    2017年,苹果并没有因为新年的气氛而对CP们"网开一面".频繁锁榜.调整排名规则以及关键词覆盖算法--不断抛出的大动作,让CP们叫苦不迭.且从1月初开始,苹果还进一步加强了对应用 ...

  7. spring 动态创建数据源

    项目需求如下,公司对外提供服务,公司本身有个主库,另外公司会为每个新客户创建一个数据库,客户的数据库地址,用户名,密码,都保存在主数据库中.由于不断有新的客户加入,所以要求,项目根据主数据库中的信息, ...

  8. Rookey.Frame v1.0 视频教程发布了

    经过昨天几个小时的折腾, Rookey.Frame v1.0开发视频教程终于发布了,由于是第一次做视频有很多地方做的不够好,后续我会慢慢改进,争取将视频教程做好. 本期发布视频: (一)Rookey. ...

  9. Angularjs快速入门(四)-css类和样式

    例子: .error{background-color:red;} .warning{background-color:yellow;} <div ng-controller='HeaderCo ...

  10. Java中遍历Map的常用方法

    以下方法适用于任何map实现(HashMap, TreeMap, LinkedHashMap, Hashtable, 等等): 方式一(推荐): // 推荐 // 在for-each循环中使用entr ...