【面向对象设计原则】之接口隔离原则(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)七大原则
这篇文章我会不停的维护它,它将会越来越长,但它是关于我在面向对象中的一些学习的思考心得.希望对自己对各位都能实用处. 开篇前,说明一下写这篇文章的原因.原因是由于设计模式.由于设计模式里的各种 ...
随机推荐
- nginx反向代理的nginx.conf配置
下面的配置是nginx.conf的示例 nginx反向代理 就是说把跨域的url通过本地代理的方式,变成同域的请求,如此来解决跨域问题 该配置下 通过http://localhost/html5/路径 ...
- iTunes制作iPhone手机铃声方法(mac版2017年4月更新)
iTunes制作iPhone手机铃声方法(mac版2017年4月更新) 跟之前百度出来的不同,我自己使用后写的. 1.首先下载好你需要制作铃声的mp3文件,这里我放在桌面. 2 ...
- 拍照、本地图片工具类(兼容至Android7.0)
拍照.本地图片工具类:解决了4.4以上剪裁会提示"找不到文件"和6.0动态授予权限,及7.0报FileUriExposedException异常问题. package com.hb ...
- ES6 对let声明的一点思考
说到ES6的let变量声明,我估计很多人会想起下面几个主要的特点: 没有变量声明提升 拥有块级作用域 暂时死区 不能重复声明 很多教程和总结基本都说到了这几点(说实话大部分文章都大同小异,摘录的居多) ...
- node.js系列(实例):原生node.js实现静态资源管理
/** * node入门之综合案例(一):简易路由 * @Author : by Ghost * @Date : 2016/07/11 * @Description : * 1.引入以下模块 * ht ...
- stm32通过电调带动电机(可按键调速)
这几天在做32通过电调带动电机的实验,上网一查,发现这方面的资料很少,经过自己的亲自实践,总结出以下经验,供大家参考. 论坛上也有很多人说自己在做,但是都遇到了同样的瓶颈.我想他们大多是pwm的频率和 ...
- Flask 学习笔记
Flask 是一个Web应用框架,我也就是一边看书,一边写博文做记录 这本书: 首先安装Flask ,和配置环境,参考这边博客: 然后就开始学习Flask 了. 1.Application and R ...
- 学习Java之前操作环境的安装及配置
1.根据自己的系统版本下载相应版本的JDK(Java开发运行时环境) 查看自己系统版本的方法:在桌面上右键计算机(win7,win10是此电脑,XP是我的电脑),点击属性,进入到计算机属性页面以后里面 ...
- Angularjs快速入门(四)-css类和样式
例子: .error{background-color:red;} .warning{background-color:yellow;} <div ng-controller='HeaderCo ...
- 蓝桥杯-马虎的算式-java
/* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...