面向对象设计原则实验之“接口隔离原则”

客户端不应该依赖那些它不需要的接口。

实验一

考虑一个安全系统。在这个系统中,有一些Door对象,可以被加锁和解锁,并且Door对象知道自己是开着还是关着。这个Door编码成一个接口,这样客户程序就可以使用那些符合Door接口的对象,而不需要依赖于Door的特定实现。

现在,考虑一个这样的实现,TimedDoor,如果门开着的时间过长,它就会发出警报声。为了做到这一点,TimedDoor对象需要和另一个名为Timer的对象交互。

如果一个对象希望得到超时通知,它可以调用Timer的Register函数。该函数有两个参数,一个是超时时间,另一个是指向TimerClient对象的引用,其TimeOut函数会在超时到达时被调用。

怎样将TimerClient类和TimedDoor类联系起来,才能在超时时通知到TimedDoor中相应的处理代码呢?比如下面的一种实现:

这种做法的问题是,现在Door类依赖于TimerClient了。可是并不是所有种类的Door都需要定时功能。事实上,最初的Door抽象类和定时功能没有任何关系。如果创建了无需定时功能的Door的派生类,那么在这些派生类中就必须要提供TimeOut方法的退化实现,这就有可能违反LSP。此外,使用这些派生类的应用程序即使不使用TimerClient类的定义,也必须要引入它。

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

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

请根据接口隔离原则,重构上面的设计。

解析(参考):

一个解决方案是**创建一个派生自TimerClient的对象,并把对该对象的请求委托给TimedDoor。**当TimedDoor想要向Timer对象注册一个超时请求时,它就创建一个DoorTimerAdapter并且把它注册给Timer。当Timer对象发送TimeOut消息给DoorTimerAdapter时,DoorTimerAdapter把这个消息委托给TimedDoor。这个解决方案遵循ISP原则,并且避免了Door的客户程序和Timer之间的耦合。即使对代码清单12-3中所示的Timer进行了改变,也不会影响到任何Door的使用者。此外,TimedDoor也不必具有和TimerClient一样的接口。DoorTimerAdapter会将TimerClient接口转换成TimedDoor接口。因此,这是一个非常通用的解决方案。

实验二

某软件公司开发人员针对 CRM 系统的客户数据显示模块设计了如下图所示的 CustomerDataDisplay 接口。其中:

方法 readData() 用于从文件中读取数据;

方法 transformToXML() 用于将数据转换成 XML 格式;

方法 createChart() 用于创建图表;

方法 displayChart() 用于显示图表;

方法 createReport() 用于创建文字报表;

方法 displayReport() 用于显示文字报表。

在实际使用过程中发现该接口很不灵活。例如:如果一个具体的数据显示类无须进行数据转换(源文件本身就是 XML 格式),但由于实现了该接口,将不得不实现其中声明的 transformToXML() 方法(至少需要提供一个空实现);如果需要创建和显示图表,除了需要实现与图表相关的方法外;还需要实现创建和显示文字报表的方法。否则程序在编译时将报错。

现使用接口隔离原则对其进行重构。

解析(参考):

在本实例中,由于在接口 CustomerDataDisplay 中定义了太多方法,即该接口承担了太多职责,一方面导致该接口的实现类很庞大,在不同的实现类中都不得不实现接口中定义的所有方法,灵活性较差,如果出现大量的空方法,将导致系统中产生大量的无用代码,影响代码质量。

另一方面由于客户端针对大接口编程,将在一定程度上破坏程序的封装性,客户端看到了不应该看到的方法,没有为客户端定制接口。因此需要将该接口按照接口隔离原则和单一职责原则进行重构,将其中的一些方法封装在不同的小接口中,确保每一个接口使用起来都较为方便,并都承担某一单一角色,每个接口中只包含一个客户端(如模块或类)所需的方法即可。

相关推荐
努力的悟空1 小时前
国土变更调查拓扑错误自动化修复工具的研究
运维·自动化
旦沐已成舟1 小时前
DevOps-Jenkins-新手入门级
服务器
周末不下雨2 小时前
win11+ubuntu22.04双系统 | 联想 24 y7000p | ubuntu 22.04 | 把ubuntu系统装到1T的移动固态硬盘上!!!
linux·运维·ubuntu
软件技术员2 小时前
Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书
服务器·网络协议·ssl
耗同学一米八3 小时前
2024 年河北省职业院校技能大赛网络建设与运维赛项样题四
运维·网络
一条晒干的咸魚3 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
东华果汁哥3 小时前
【linux 免密登录】快速设置kafka01、kafka02、kafka03 三台机器免密登录
linux·运维·服务器
肖永威4 小时前
CentOS环境上离线安装python3及相关包
linux·运维·机器学习·centos
mengao12344 小时前
centos 服务器 docker 使用代理
服务器·docker·centos
布鲁格若门4 小时前
CentOS 7 桌面版安装 cuda 12.4
linux·运维·centos·cuda