如果对象认识很多所有者,这是代码的味道吗?


9

在我们的Delphi 2007应用程序中,我们使用了许多以下构造

FdmBasic:=TdmBasicData(FindOwnerClass(AOwner,TdmBasicData));

FindOwnerClass向上移动当前组件的Owner层次结构以查找特定的类(在示例TdmBasicData中)。结果对象存储在Field变量FdmBasic中。我们主要使用它来传递数据模块。

示例:生成报告时,结果数据将被压缩并存储在通过数据模块TdmReportBaseData访问的表的Blob字段中。在我们应用程序的单独模块中,具有使用ReportBuilder以Paged形式显示来自报表数据的功能。该模块的主要代码(TdmRBReport)使用TRBTempdatabase类将压缩的Blob数据转换为Reportbuilder运行时reportdesigner中可用的不同表。TdmRBReport可以访问TdmReportBaseData,以获取所有与报告相关的各种数据(报告类型,报告计算设置等)。TRBTempDatabase是在TdmRBReport中构造的,但必须有权访问TdmReportBasedata。因此,现在可以使用上面的构造完成此操作:

constructor TRBTempDatabase.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);

  FdmReportBaseData := TdmRBReport(FindOwnerClass(Owner, TdmRBReport)).dmReportBaseData;
end;{- .Create }

我的感觉是,这意味着TRBTempDatabase非常了解其所有者,并且我想知道这是某种代码味道还是反模式。

您对此有何想法?这是代码气味吗?如果是这样,还有什么更好的方法?


1
如果应该对另一门课有很多了解,那么将提供一种更简单的方法。
洛伦·佩希特尔

Answers:


13

这种外观类似于由马丁·福勒(Martin Fowler)首次描述的服务定位器模式(已被确定为常见的反模式)。

与服务定位器相比,基于构造的依赖注入更可取,因为它可以提高需求参数的可见性并简化单元测试。

使用服务定位器的问题不在于您是否依赖于特定的服务定位器实现(尽管这也可能是一个问题),而是真正的反模式。这将给您的API使用者带来可怕的开发人员体验,并使您作为维护开发人员的生活变得更糟,因为您将需要使用大量的脑力来掌握所做的每一次更改的含义。

使用构造函数注入时,编译器可以为消费者和生产者提供大量帮助,但是对于依赖于Service Locator的API而言,这些帮助都不可用。

最值得注意的是,它也违反了得墨meter耳定律

得墨meter耳定律(LoD)或最不知识原理是用于开发软件(尤其是面向对象的程序)的设计指南。在一般形式下,LoD是松耦合的一种特殊情况。

功能的德米特定律要求对象O的方法M只能调用以下类型的对象的方法:

  1. O本身
  2. M的参数
  3. 在M中创建/实例化的任何对象
  4. O的直接组成对象
  5. M范围内的O可以访问的全局变量

特别是,对象应避免调用另一个方法返回的成员对象的方法。对于许多使用点作为字段标识符的现代面向对象语言,可以将法律简单地表述为“仅使用一个点”。也就是说,代码abMethod()违反了a.Method()没有的法律。举一个简单的例子,当一个人要walk狗时,命令狗的腿直接走路是愚蠢的。取而代之的是命令狗,让它照顾自己的腿。

更好的方法

实际上,更好的方法是删除类中的服务定位器调用,并将正确的所有者作为参数传入其构造函数中。即使这意味着您有一个执行所有者查找的服务类,然后将其传递给类构造函数

constructor TRBTempDatabase.Create(aOwner: TComponent, ownerClass: IComponent);
begin
  inherited Create(aOwner);

  FdmReportBaseData := TdmRBReport(ownerClass).dmReportBaseData;
end;{- .Create }

3
好的答案,以及给想出这个奇妙比喻的人的道具:As a simple example, when one wants to walk a dog, it would be folly to command the dog's legs to walk directly; instead one commands the dog and lets it take care of its own legs.
安迪·亨特

3

让子对象对父母了解得太多的困难之一是,您最终会实现可能(并且通常确实)变得过于紧密的耦合模式,这会导致严重的依赖问题,并且以后常常很难安全地进行修改和维护。稍后的。

根据两个类之间的连接深度,听起来有点像Fowler对功能嫉妒或不适当的亲密关系代码的描述所见。

似乎需要使用数据加载或读取类,在这种情况下,您可以使用多种替代模式来打破孩子与其父链之间的依赖关系,这听起来像您需要委派访问任务您的数据类,而不是让数据访问器类自己负责所有事情。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.