敏捷开发笔记(第12章节)--接口隔离原则(ISP)

news2024/9/20 14:41:04

目录

 

1:PDF上传链接

 12.1:接口污染

 12.2:分离客户就是分离接口

 12.3:接口隔离原则(ISP)

 12.4:类接口与对象接口

 12.4.1:使用委托分离接口

 12.4.2:使用多重继承分离接口

结论


 

1:PDF上传链接

【免费】敏捷软件开发(原则模式与实践)资源-CSDN文库

        这个原则用来处理“胖(fat)”接口所具有的缺点。如果类的接口不是内聚的(cohesive),就表示该类具有“胖”的接门。换句话说,类的“胖”接口可以分解成多组方法。每一组方法都服务于一组不同的客户程序。这样,一些客户程序可以使用一组成员函数,而其他客户程序可以使用其他组的成员函数。
        ISP承认存在有一些对象,它们确实不需要内聚的接口:但是SP建议客户程序不应该看到它们作为单一的类存在。相反,客户程序看到的应该是多个具有内聚接口的抽象基类。

 12.1:接口污染

        考虑一个安全系统。在这个系统中,有一些Door对象,可以被加锁和解锁,并且Doo对象知
道自己是开着还是关着。(参见程序12.1。)

程序12.1安全系统中的Door
class Door
{
    public:
        virtual void Lock() = 0:
        virtual void Unlock() = 0;
        virtual bool IsDoorOpen() = 0:
}

        该类是抽象的,这样客户程序就可以使用那些符合Doo接口的对象,而不需要依赖于Doo的
特定实现。
        现在,考虑一个这样的实现,TimedDoor,.如果门开着的时间过长,它就会发出警报声。为了做到这一点,TimedDoor对象需要和另一个名为Timer的对象交互。(参见程序12.2。)】

程序12.2

class Timer
{
    public:
    void Register(int timeout, Timerclient *client);
};

class Timerclient
{
    public:
        virtual void Timeout() = 0;
};

        如果一个对象希望得到超时通知,它可以调用Timer的Register函数。该函数有两个参数,一个是超时时间,另个是指向TimerClient对象的指针,该对象的TimeOut函数会在超时到达时被调用。
        我们怎样将TimerClient类和TimedDoor类联系起来,才能在超时时通知到TimedDoor中相应的处理代码呢?有几个方案可供选择。图12.1中展示了一个易想到的解决方案。其中D00r继承了TimerClient,因此TimedDoor也就继承了TimeClient。这就保证了TimerClient可以把自己注册到Timer中,并且可以接收TimeOut消息。

图12.1 

        虽然这个解决方案很常见,但是它也不是没有问题。最主要的问题是,现在Door类依赖于TimerClient了。可是并不是所有种类的Doo都需要定时功能。事实上,最初的Do0抽象类和定时功能没有任何关系。如果创建了无需定时功能的Door的派生类,那么在这些派生类中就必须要提供TimeOut方法的退化(degenerate)实现这就有可能违反LSP。此外,使用这些派生类的应用程序即使不使用TimerClient类的定义,也必须要引入(import)它。这样就具有了不必要的复杂性以及不必要的重复的臭味。

        这是一个接口污染的例子,这种情况在像C+、Java这样的静态类型语言中是很常见的。Door的接口被一个它不需要的方法污染了。在D00的接口中加入这个方法只是为了能给它的一个子类带来好处。如果持续这样做的话,那么每次子类需要一个新方法时,这个方法就会被加到基类中去。
这会进一步污染基类的接口,使它变“胖”。
        此外,每次基类中加入一个方法时,派生类中就必须要实现这个方法(或者定义一个缺省实现)。事实上,有一种特定的相关实践,可以使派生类无需实现这些方法,该实践的做法是把这些接口合并为一个基类,并在这个基类中提供接口中方法的退化实现。但是按照我们前面所学习的,这种实践违反了LSP,带来了维护和重用方面的问题。

 12.2:分离客户就是分离接口

        Door接口和TimerClient接口是被完全不同的客户程序使用的。Timer使用TimerClient,.而操作门的类使用D00。既然客户程序是分离的,所以接口也应该保持分离。为什么呢?因为客户程序对它们使用的接口施加有作用力。

 12.3:接口隔离原则(ISP)

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

 12.4:类接口与对象接口

        再次考虑-一下TimedDoor问题。这里有一个具有两个独立的接口,由两个独立的客户一Timer以及Doo所使用的对象。因为实现这两个接口需要操作同样的数据,所以这两个接口必须在同~个对象中实现。那么怎样才能遵循1SP呢?怎样才能分离必须在起实现的接口呢?

        该问题的答案基于这样的事实,就是一个对象的客户不是必须通过该对象的接口去访问它,也可以通过委托或者通过该对象的基类去访问它。

 12.4.1:使用委托分离接口

        一个解决方案是创建一个派生自TimerClient的对象,并把对该对象的请求委托给TimedDoor。图12.2展示了这个解决方案。
        当TimedDoor想要向Timer对象注册一个超时请求时,它就创建-个DoorTimerAdapter并且把它注册给Timer。当Timer对象发送TimeOut消,息给DoorTimerAdapter时,DoorTimerAdapter把这个消息委托给TimedDoor。

图12.2 Door定时器适配器 

        这个解决方案遵循ISP原则,并且避免了Door的客户程序和Timr之间的耦合。TimedDoor也不必具有和TimerClient一样的接口。DoorTimerAdapter会将TimerClient接口转换成TimedDoor接口。因此,这是一个非常通用的解决方案。(参见程序12.4。)

程序12.4 TimedDoor.cpp

class TimedDoor: public Door
{
    public:
        virtual void DoorTimeOut (int timeOutID);
}:

class DoorTimeAdapter: public Timerclient
{
    public:
    DoorTimerAdapter (TimedDoor& theDoor): itsTimedDoor (theDoor)
    {}

    vistual void TimeOut (int timeoutId)
    {
        itsTimedDoor.DoorTimeout (timeOutId);
    }

    private:
        TimedDoor&itsTimedDoor;
}

        不过,这个解决方案还是有些不太优雅。每次想去注册一个超时请求时,都要去创建一个新的对象。此外,委托处理会导致一些很小但仍然存在的运行时间和内存的开销。有一些应用领域,比如嵌入式实时控制系统,其中内存和运行时间都是非常宝贵的,以至于这种开销成了一个值得关注的问题。

 12.4.2:使用多重继承分离接口

        图12.3和程序12.5展示了如何使用多重继承来达到符合ISP的目标。在这个模型中,TimerdDoor同时继承了Door和TimerClient。.尽管这两个基类的客户程序都可以使用TimedDoor,但是实际上却都不再依赖于TimedDoor类。这样,它们就通过分离的接口使用同一个对象。

图12.3多重继承的Timed Door

程序12,5 TimedDoor.cpp
class TimedDoor: public Door, pubiic Timerclient
{
    public:
        virtual void DoorTimeOut (int timeOutID);
}

        通常会优先选择这个解决方案。只有当DoorTimerAdapter对象所做的转换是必须的,或者不同的时候会需要不同的转换时,我才会选择图12.2中的方案而不是图12.3中的方案。

结论

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1937137.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于JAVA+SpringBoot+Vue+uniApp的校园日常作品商品分享小程序

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、SpringCloud、Layui、Echarts图表、Nodejs、爬…

为什么精酿人士选择FENDI CLUB啤酒

FENDI CLUB啤酒作为云仓酒庄平台的精酿啤酒自有品牌,平台提供以技术咨询与服务为核心的全产业链精酿产品解决方案,帮助啤酒代理商提高产品质量,树立优质品牌,推广精酿文化,促进精酿发展,力争成为中国本土品…

Leetcode3208. 交替组 II

Every day a Leetcode 题目来源:3208. 交替组 II 解法1:环形数组 把数组复制一份拼接起来,和 3101 题一样,遍历数组的同时,维护以 i 为右端点的交替子数组的长度 cnt。 如果 i ≥ n 且 cnt ≥ k,那么 i…

基于springboot+vue+uniapp的开放实验室预约管理系统

开发语言:Java框架:springbootuniappJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包&#…

算法第九天:leetcode59.螺旋矩阵II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1: 输入:n 3 输出:[[1,2,3],[8,9,4],[7,6,5]]示例 2: 输入:n 1 输出&am…

如何高效删除 JavaScript 数组中的重复元素?

在日常编程中,我们经常会遇到数组去重的问题。今天,我们就来聊聊如何用JavaScript来优雅地解决这个问题。 问题描述 给定一个包含重复元素的数组,我们希望创建一个新的数组,其中只包含原始数组中的唯一值。例如,如果我…

十二、数组(2)

1.冒泡排序数组(升序) 冒泡排序:将一个整型数组排序(升序) 例: 10 9 8 7 6 5 4 3 2 1 9 10 8 7 6 …

Android11 framework 禁止三方应用开机自启动

Android11应用自启动限制 大纲 Android11应用自启动限制分析验证猜想:Android11 AOSP是否自带禁止三方应用监听BOOT_COMPLETED​方案禁止执行非系统应用监听到BOOT_COMPLETED​后的代码逻辑在执行启动时判断其启动的广播接收器一棍子打死方案(慎用&#…

【Windows】操作系统之进程(第二篇)

目录 一、程序与进程的区别 一、定义与概念 二、主要区别 三、总结 二、进程的空间分配 1. 栈区(Stack) 2. 堆区(Heap) 3. 全局区(静态区,Static Area) 4. 文字常量区(Text …

抓紧上车!中国学者用最新数据发一区top | GBD数据库周报(7.10~7.16)

全球疾病负担(GBD)是迄今为止规模最大、最全面的一项研究,旨在量化不同地区和不同时期的健康损失,从而改善卫生系统并消除差异。 该研究由华盛顿大学健康指标与评估研究所 (IHME) 牵头,是一项真正的全球性研究&#xf…

JVM:常用工具总结

文章目录 一、jstat工具 一、jstat工具 Jstat工具是JDK自带的一款监控工具,可以提供各种垃圾回收、类加载、编译信息等不同的数据。使用方法为:jstat -gc进程ID每次统计的时间间隔(毫秒)统计次数。 C代表Capacity容量&#xff0c…

高性能系统架构设计之:多级缓存

前言 为了提高系统的性能,一般会引入“缓存机制”,将部分热点数据存入缓存中,用空间换取时间,以达到快速响应的目的。 其实,缓存的应用远远不止存在于服务层(传统的Redis缓存),从客户…

AIGC Kolors可图IP-Adapter-Plus风格参考模型使用案例

参考: https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus 代码环境安装: git clone https://github.com/Kwai-Kolors/Kolors cd Kolors conda create --name kolors python=3.8 conda activate kolors pip install -r requirements.txt python3 setup.py install…

2-39 基于matlab的二维拉普拉斯方程求解

基于matlab的二维拉普拉斯方程求解,用有限差分法求解二维拉普拉斯方程 所用的数值方案是空间二阶中心差分法 (5 点差分)。输出三维求解结果。程序已调通,可直接运行。 2-39 matlab 二维拉普拉斯方程求解 - 小红书 (xiaohongshu.com)

PDF-Extract-Kit (PDF内容抽取开源项目)

Github 地址:https://github.com/opendatalab/PDF-Extract-Kit 整体介绍 PDF文档中包含大量知识信息,例如文本、表格、图像、公式等。此外,PDF的文档布局也相当复杂,页眉、页脚、表格标题、图片标题等等,提取高质量的…

Nature子刊 | ATAC-seq、RNA-seq和蛋白组联合分析揭示脂质激活转录因子PPARα在肾脏代偿性肥大的作用机制

2023年6月,美国国立心肺血液研究所的研究团队在Nature Communications上发表题为“Signaling mechanisms in renal compensatory hypertrophy revealed by multi-omics”的文章,该研究通过在单侧肾切除的小鼠模型中使用多组学方法(蛋白质组学…

十一、面向对象进阶

文章目录 学习目标一、类方法和静态方法二、单例模式三、Python的继承3.1 继承的基本使用3.2 Python继承的特点3.3 私有属性的继承特点3.4 新式类和经典类四、对象相关的运算符和内置函数五、多态的使用5.1 子类重写父类的方法5.2 多态的使用学习目标 说出类方法和实例方法的区…

Spring中的IOC详解

文章目录 IOCIOC容器的工作原理Bean的生命周期Bean的自动装配AutowiredResourceInject 使用Spring底层组件 IOC Spring的核心之一是IOC,IOC全称为Inversion of Control,中文译为控制反转,是面向对象编程中的一种设计原则,可以用来…

栈和队列(一) ------基本概念,循环队列

目录 栈 python实现 队列 python实现 循环队列 力扣622- --循环队列 力扣20 ----有效括号判断 分析 代码 栈 python实现 在Python中实现一个栈(Stack)可以通过使用列表(list)来完成,因为列表提供了动态数…

【网络安全的神秘世界】Error:Archives directory /var/cache/apt/archives/partial is missing.

🌝博客主页:泥菩萨 💖专栏:Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 ✨问题描述 在kali中想要安装beef-xss软件包时,发生如下报错: Error: Archives directory /var/cac…