【问题标题】:Implementing the interface vs a method returning an object that implements the interface实现接口与返回实现接口的对象的方法
【发布时间】:2014-01-18 07:50:24
【问题描述】:

为了提高我的设计技能,我最近热衷于面向对象的设计。这个问题是关于我经常看到的特定设计选择,但不明白其中的原理。我知道设计选择倾向于主观,但我想知道其他人对此有何看法,以了解我的设计直觉是变好还是变坏。

我在看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 页面的开发人员不需要重新实现爬取功能,即无论 wiki 页面的实现如何,爬行功能都应该是相同的。这是这样设计的目的,还是我遗漏了什么?

我在 SO 上找到了Implementing an interface vs. providing an interface 问题,但它并不完全相同,并且没有提供太多关于何时可以设计此类东西的见解。

【问题讨论】:

    标签: oop design-patterns interface


    【解决方案1】:

    您所看到的是正在实施的接口隔离原则。你说 PageCrawler 的界面让你认为“这些都是属于 WikiPage 的东西”,但这是从实现的角度来看事情,而不是从调用 WikiPage 的人的角度来看。

    编辑: 也许不是接口隔离原则,而是单一职责原则。

    【讨论】:

    • 谢谢乔恩,我没有这样想。但是,我对 ISP 的理解是不同的。我认为 ISP 将职责分离为有凝聚力的接口并通过实现接口来组合对象,而不一定从实现接口的方法返回对象。在这种情况下,我认为PageCrawler接口的存在本身就是ISP的使用。如果WikiPage 实现了它,PageCrawler 接口的客户端仍然不知道实现者是WikiPage
    • 此外,听起来您是在说 WikiPage 的客户不应该知道 PageCrawler 方法(这就是您的意思吗?),但他们仍然可以访问它们,除了现在他们依赖于看似多余的信息 (getPageCrawler)。使用装饰器不是更好吗,例如,让WikiPage 实现PageCrawler,然后WikiPage 可以选择在内部委托给一个单独的对象,该对象也实现PageCrawler?我想我的观点是:他们说WikiPage 有一个PageCrawler,但对我来说WikiPage可抓取
    【解决方案2】:

    在某些情况下,某些与对象“关联”的函数需要保持与对象本身分开的状态。在 .NET 中,IEnumerator<T> 方法就是最好的例子。它们的含义通常来自关联的IEnumerable<T>,但每个枚举器都有一个状态,应该与底层集合的状态分开(尤其是IEnumerable<T> 的典型实现将无法知道有多少个枚举器,每个枚举器都有一个独立状态,可以同时关联。

    分离接口实现的更多优点:

    • 拆分实现可以包含名称与基础类型的名称相同但功能不同的成员。例如,Dictionary<TKey,TValue> 实现了IEnumerable<KeyValuePair<TKey,TValue>>,但有一个Keys 属性实现了IEnumerable<TKey>DictionaryKeys 返回的东西都有一个 GetEnumerator 方法,但是一个枚举键值对而另一个只枚举键。

    • 1234563对对象的无限制引用。在某些情况下,拥有一个对象来保存对单个包装器对象的引用,然后可以使用它来满足任意数量的请求,这可能比让客户端使用包装器对象的构造函数为每个包装器对象创建一个新的包装器对象更有效。请求。
    • 尽管 Java 和 .NET 都不允许双重继承,但每个紧密关联的对象都有可能从不同的类继承。

    我不熟悉你提到的特定类型,所以我不知道它们行为的具体原因,但提到的原因都是常见的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-16
      • 1970-01-01
      • 2011-06-22
      • 1970-01-01
      • 2012-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多