我最近一直在进行面向对象的设计狂欢,以提高我的设计技能。这个问题是关于我经常看到的一个特定的设计选择,并且不理解其基本原理。我知道设计选择倾向于主观,但我想知道其他人对此的看法,以确定我的设计本能是变得更好还是更差。
我在看Robert C Martin(Uncle Bob) -Clean Architecture and Design-2012 COHAA The Path to Agility Conference。在演讲中,他讲述了一个关于发展Fitnesse的故事。我不熟悉这个软件,所以我查阅了the project hosted on github。
在查看项目时,有一件事引起了我的注意:WikiPage
interface:PageCrawler getPageCrawler();
方法。所以我查看PageCrawler
interface以查看它的外观。在检查此界面后,我认为PageCrawler
中的方法看起来属于WikiPage
,而WikiPage
可以合理地实现该界面。
我认为将两者分开可能会导致WikiPage
公开内部,以便抓取页面所需的信息可以被抓取的对象访问。此外,BaseWikiPage
抽象类只返回一个新的PageCrawlerImpl
,项目中没有其他PageCrawler
实现,我可以看到。
我在其他项目中看到过这种类型的代码,其中一个接口/类的方法返回另一个接口/类的对象,其方法可以合理地属于第一个类。在试图了解Fitnesse开发人员的意图时,我想出这个设计的唯一原因是,通过实现WikiPage
创建新wiki页面的开发人员不需要重新实现爬网功能,即,无论维基页面的实现如何,爬行功能都应该相同。这是这种设计的目的,还是我错过了什么?
我在SO上发现了Implementing an interface vs. providing an interface问题,但它并不完全相同,并且没有深入了解何时可以设计这样的内容。
答案 0 :(得分:2)
您所看到的是界面隔离原则。你说PageCrawler的界面会让你觉得“这些都是属于WikiPage的东西”,但是从实现的角度看待事物,而不是某人调用WikiPage的观点。
编辑: 也许它不是关于界面隔离原则,而是关于单一责任原则的更多内容。
答案 1 :(得分:1)
在某些情况下,某些与对象“关联”的函数需要保持应该与对象本身分离的状态。在.NET中,IEnumerator<T>
方法就是这方面的主要例子。它们的含义通常来自关联的IEnumerable<T>
,但每个枚举器的状态应与底层集合的状态分开(特别是IEnumerable<T>
的典型实现无法知道有多少枚举器,每个都有独立的状态,可以同时与它相关联。
分割接口实现的一些优点:
拆分实现可以包括名称与基础类型相同但功能不同的成员。例如,Dictionary<TKey,TValue>
实现IEnumerable<KeyValuePair<TKey,TValue>>
,但具有实现Keys
的{{1}}属性。 IEnumerable<TKey>
和Dictionary
返回的内容都有一个Keys
方法,但是一个枚举键值对,而另一个只枚举键。
如果拆分实现包装了底层对象,并且没有公开对它的引用但是暴露了它的一些功能,那么它可以安全地暴露给那些受信任的有限功能的代码,但不是对对象的不受约束的引用。在某些情况下,让一个对象持有对单个包装器对象的引用(然后可以用来满足任意数量的请求)可能比让客户端使用包装器对象的构造函数为每个创建一个新的包装器对象更有效。请求。
虽然Java和.NET都不允许双重继承,但每个紧密关联的对象都可以从不同的类继承。
我不熟悉你提到的特定类型,所以我不知道他们表现出来的具体原因,但提到的原因都是常见的。