第12章 ISP:接口隔离原则

  不应该强迫客户程序依赖并未使用的方法。  

  这个原则用来处理“胖”接口所存在的缺点。如果类的接口不是内敛的,就表示该类具有“胖”接口。换句话说,类的“胖”接口可以分解成多组方法。每一组方法都服务于一组不同的客户程序。这样,一些客户程序可以使用一组成员函数,而其他客户程序可以使用其他组的成员函数。

  ISP承认一些对象确实需要非内敛的接口,但是ISP建议客户不应该看到它们作为单一的类存在。相反,客户程序看到的应该是多个具有内敛接口的抽象基类。

12.1 接口污染

  如果子类的接口中有子类不需要的方法,就说这个接口被污染了。在接口中加入方法,只为了能给它的其中一个子类带来好处。如果持续这样的话,那么每次子类需要一个新方法时,这个方法就会加到基类中去。这会进一步污染其基类的接口,使它变“胖”。

  每次基类中加入一个方法时,派生类中就必须实现这个方法(或者定义一个默认的实现)。事实上,一种特定的相关实践,可以使派生类无需实现这些方法,该实践的方法就是把这些接口合并成一个基类,并在这个基类中提供接口中方法的退化实现。但是,这种实践违反了LSP,会带来维护和重用方面的问题。

12.2 分离客户就是分离接口

  不应该强迫客户程序依赖并未使用的方法。如果强迫客户依赖于它们不使用的方法,那么这些客户程序就面临着由于这些未使用方法的改变带来的变更。这无意中导致了所有客户程序之间的耦合。换种说法,如果一个客户程序依赖于一个含有它不使用的方法的类,但是其它客户程序却要使用该方法,那么当其他客户程序要求这个类改变时,就会影响到这个客户程序。我们希望尽可能的避免这种耦合,因此我们希望分类接口。

12.3 类接口与对象接口

  一个对象的客户程序不必通过该对象的接口来访问它,也可以通过委托或者通过该对象的基类来访问它。

12.3.1 使用委托分离接口

  这里的“委托”不是我们说的“委托类型”的委托,原文翻译的容易产生歧义。其实就是用适配器来解耦客户程序和接口。

  不过,这个解决方案还有些不太优雅。每次想要注册一个请求,都要去创建一个新的对象。

12.3.2 使用多重继承分离接口

  通常我会优先选择这个解决方案。只有当Adapter对象所做的转换是必须的,或者不同的时候需要不同的转换时,才使用适配器的方案。

12.4 ATM用户界面的例子

  ATM(自动取款机)需要一个非常灵活的界面。它的输出信息需要转换成不同的语言。输出信息可能显示在屏幕上,或者盲文书写板,或者通过语音合成器说出来。通过创建一个抽象类就可以实现这种需求:

  同样可以把每个ATM执行的不同事务(存储、取款、转账)封装为类Transaction的派生类:

  注意,这正好是LSP告我我们应该避免的情形。每个事务所使用的UI的方法,其他操作类都不会使用,这样对于任何一个Transaction的派生类的改动都会迫使对UI的相应改动,从而也影响到了其他所有Transaction的派生类及其他所有依赖于UI接口的类。这样的设计就有了僵化性和脆弱性。

  这时需要分类ATM的UI接口:

  这样,每次创建一个Transaction类的新派生类,抽象接口UI就需要增加一个相应的基类,并且因此UI接口以及所有它的派生类都必须改变。不过,这些类并没有被广泛使用。事实上,它们可能仅被main或者那些启动系统并创建具体UI实例之类的过程使用。因此,增加新的UI基类所带来的影响被减至最小。

  查看如下代码:

public interface Transaction
{
void Execute();
}
public interface DepositUI
{
void RequestDepositAmount();
}
public class DepositTransaction : Transaction
{
privateDepositUI depositUI;
public DepositTransaction(DepositUI ui)
{
depositUI = ui;
}
public virtual void Execute()
{
/*code*/
depositUI.RequestDepositAmount();
/*code*/
}
}
public interface WithdrawalUI
{
void RequestWithdrawalAmount();
}
public class WithdrawalTransaction : Transaction
{
private WithdrawalUI withdrawalUI;
public WithdrawalTransaction(WithdrawalUI ui)
{
withdrawalUI = ui;
}
public virtual void Execute()
{
/*code*/
withdrawalUI.RequestWithdrawalAmount();
/*code*/
}
}
public interface TransferUI
{
void RequestTransferAmount();
}
public class TransferTransaction : Transaction
{
private TransferUI transferUI;
public TransferTransaction(TransferUI ui)
{
transferUI = ui;
}
public virtual void Execute()
{
/*code*/
transferUI.RequestTransferAmount();
/*code*/
}
}
public interface UI : DepositUI, WithdrawalUI, TransferUI
{
}

  每个事务都必须以某种方式知晓它的特定UI版本。使每个事务在构造时给它传入特定于它的UI的引用就解决了这个问题。这使我们可以使用如下代码:

UI Gui; // global object;
void f()
{
DepositTransaction dt = new DepositTransaction(Gui);
}

  虽然这很方便,但是同样要求每个事务都有一个整型对应UI的引用成员。在C#中,一种比较有诱惑力的做法是把所有UI组件放到一个单一的类中,如下:

public class UIGlobals
{
public static WithdrawalUI withdrawal;
public static DepositUI deposit;
public static TransferUI transfer;
static UIGlobals()
{
UI Lui = new AtmUI(); // Some UI implementation
UIGlobals.deposit = Lui;
UIGlobals.withdrawal = Lui;
UIGlobals.transfer = Lui;
}
}

  不过,这种做法有一个负面效果。那就是UIGlobal类依赖于DepositUI、WithdrawalUI和TransferUI。UIGlobal类把我们分离的接口重新结合在一起了。

  每个原则在应用时都必须小心,不能过度使用它们。如果一个类具有数百个不同接口,其中一些是根据客户程序分离的,另一些是根据版本分离的,那么该类就是难以琢磨的,这种难以琢磨性是非常令人恐惧的。

12.5 结论
  胖类会导致它们的客户程序之间产生不正常的并且有害的耦合关系。当一个客户程序要求改胖类进行一个改动时,会影响到所有其他的客户程序。因此,客户程序应该仅仅依赖于它们实际调用的方法。通过把胖类的接口分解为多个特定于用户程序的接口,可以实现这个目标。每个特定于客户程序的接口仅仅声明它的特定客户或者客户组调用的那些函数。接着,该胖类就可以继承所有特定于客户程序的接口,并实现它们。这就解除了客户程序和它们没有调用的方法间的依赖关系,并使客户程序之间互不依赖。

摘自:《敏捷软件开发:原则、模式与实践(C#版)》Robert C.Martin    Micah Martin 著

转载请注明出处:

作者:JesseLZJ
出处:http://jesselzj.cnblogs.com

敏捷软件开发:原则、模式与实践——第12章 ISP:接口隔离原则的更多相关文章

  1. 设计模式原则(4)--Interface Segregation Principle(ISP)--接口隔离原则

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.定义: 使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口. 2.使用场景: 类A ...

  2. 八、ISP 接口隔离原则

    ISP应用的场景是某些类不符合SRP原则,但使用这些类的客户端应该根据它们的父类来使用(我感觉这句话应该改为:客户端应该根据它们的抽象类\接口来使用它们),而不是直接使用它们. 定义: 客户端不应该依 ...

  3. 六大设计原则(四)ISP接口隔离原则(上)

    ISP的定义 首先明确接口定义 实例接口 我们在Java中,一个类用New关键字来创建一个实例.抛开Java语言我们其实也可以称为接口.假设Person zhangsan = new Person() ...

  4. ISP接口隔离原则

    一.定义 不应该强迫客户程序依赖并未使用的方法 二.接口污染 接口污染,在C#.C++这样的静态类型语言中是很常见的.一个接口会被他不需要的方法污染.在接口中假如一个方法只是为了能给它的一个子类带来好 ...

  5. 【Scrum】-NO.40.EBook.1.Scrum.1.001-【敏捷软件开发:原则、模式与实践】- Scrum

    1.0.0 Summary Tittle:[Scrum]-NO.40.EBook.1.Scrum.1.001-[敏捷软件开发:原则.模式与实践]- Scrum Style:DesignPattern ...

  6. 《敏捷软件开发-原则、方法与实践》-Robert C. Martin读书笔记(转)

    Review of Agile Software Development: Principles, Patterns, and Practices 本书主要包含4部分内容,这些内容对于今天的软件工程师 ...

  7. 敏捷软件开发_实例2<四>

    敏捷软件开发_实例2 上一章中对薪水支付案例的用例和类做了详细的阐述,在本篇会介绍薪水支付案例包的划分和数据库,UI的设计. 包的划分 一个错误包的划分 为什么这个包是错误的: 如果对classifi ...

  8. C#软件设计——小话设计模式原则之:接口隔离原则ISP

    前言:有朋友问我,设计模式原则这些东西在园子里都讨论烂了,一搜一大把的资料,还花这么大力气去整这个干嘛.博主不得不承认,园子里确实很多这方面的文章,并且不乏出色的博文.博主的想法是,既然要完善知识体系 ...

  9. 第2章 面向对象的设计原则(SOLID):4_接口隔离原则(ISP)

    4. 接口隔离原则(Interface Segregation Principle,ISP) 4.1 定义 (1)使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口.类间的 ...

随机推荐

  1. 字符串js编码转换成实体html编码的方法(防范XSS攻击)

    js代码在html页面中转换成实体html编码的方法一: <!DOCTYPE html><html> <head>    <title>js代码转换成实 ...

  2. SQL Server中的事务日志管理(3/9):事务日志,备份与恢复

    当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...

  3. visual studio 局域网远程调试web项目

    1.进入项目根目录,找到.vs/config/applicationhost.config文件(可能是隐藏的) 2.搜索sites节点,找到当前项目,并添加一个binding配置节,将ip地址设置为本 ...

  4. 收集了50道基础的java面试题

    下面的内容是对网上原有的Java面试题集及答案进行了全面修订之后给出的负责任的题目和答案,原来的题目中有很多重复题目和无价值的题目,还有不少的参考答案也是错误的,修改后的Java面试题集参照了JDK最 ...

  5. 10个Web设计的SEO规则

    规则0:不要试图作*弊来提升SEO效果.当你踏入一个房间:里面汇集了手拿各种博士文凭的科学家,你认为你会聪明过他们吗?当然不会.Google和百度拥有成百上千个这样的房间,里面的工作人员都是高学历的技 ...

  6. [CLR via C#]16. 数组

    数组是允许将多个数据项当作一个集合来处理的机制.CLR支持一维数组.多维数组和交错数据(即由数组构成的数组).所有数组类型都隐式地从System.Array抽象类派生,后者又派生自System.Obj ...

  7. 与众不同 windows phone (41) - 8.0 相机和照片: 通过 AudioVideoCaptureDevice 捕获视频和音频

    [源码下载] 与众不同 windows phone (41) - 8.0 相机和照片: 通过 AudioVideoCaptureDevice 捕获视频和音频 作者:webabcd 介绍与众不同 win ...

  8. 随心所欲导出你的 UI 界面到 PDF 文件

    使用 C1PDF 控件可以导出文件到 PDF 文件,结合 .NET 平台特性你可以在任何客户端生成自定义报表.你可以打印任何 UI 界面,例如 DataGrid 导出到 PDF. 在本篇文章中我们将阐 ...

  9. Bootstrap 框架 栅格布局系统设计原理

    如果你是初次接触Bootstrap,你一定会为它的栅格布局感到敬佩.事实上,这个布局系统提供了一套响应式的布局解决方案. 既然这么好用,那他是如何用CSS来实现的呢? 我特意去Bootstrap官方下 ...

  10. vmware linux top si高以及网卡队列、软负载相关优化

    今日,测试公司自行开发的一rpc中间件,期间发现top si的比例很高,且几乎只有一个cpu是繁忙的,其他均基本为0. 经查,si主要是系统软中断,最后确定是网卡导致的系统中断.于是,往上搜了下资料, ...