当前位置:  开发笔记 > 编程语言 > 正文

如何解决违反德米特法的行为?

如何解决《如何解决违反德米特法的行为?》经验,为你挑选了3个好方法。

我和一位同事为我们的客户设计了一个系统,我们认为我们创造了一个漂亮干净的设计.但是我遇到了一些我们引入的耦合问题.我可以尝试创建一个包含与我们的设计相同的问题的示例设计,但如果您原谅我,我将创建一个设计摘录来支持这个问题.

我们正在开发一种为患者注册某些治疗方案的系统.为了避免链接到图像,我将概念性UML类图描述为ac#样式类定义.

class Discipline {}
class ProtocolKind 
{ 
   Discipline; 
}
class Protocol
{
   ProtocolKind;
   ProtocolMedication; //1..*
}
class ProtocolMedication
{
   Medicine;
}
class Medicine
{
   AdministrationRoute;
}
class AdministrationRoute {}

我将尝试解释一下设计,协议是新治疗的模板.并且协议属于某种类型并且具有需要施用的药物.根据协议,对于相同的药物(以及其他事物),剂量可以不同,因此存储在ProtocolMedication类中.AdministrationRoute是药物的管理方式,与协议管理分开创建/更新.

我发现以下地方违反了得墨忒耳法则:

违反了得墨忒耳法

在BLL里面

例如,在ProtocolMedication的业务逻辑中,存在依赖于药物的AdministrationRoute.Soluble属性的规则.代码将成为

if (!Medicine.AdministrationRoute.Soluble)
{
   //validate constrains on fields
}

在存储库内

列出某个学科中所有协议的方法将写成:

public IQueryable ListQueryable(Discipline discipline)
{
    return ListQueryable().Where(p => (p.Kind.Discipline.Id == discipline.Id)); // Entity Frameworks needs you to compare the Id...
}

在用户界面内

我们使用ASP.NET(没有MVC)作为我们系统的接口,在我看来这个层目前有最严重的违规行为.gridview的数据绑定(必须显示协议的Discipline的列必须绑定到Kind.Discipline.Name),这是字符串,因此没有编译时错误.


   
      <%# Eval("Kind.Discipline.Name")%>
   

所以我认为实际的问题可能是,什么时候可以将其视为Demeter的建议,以及可以采取哪些措施来解决违反Demeter法的问题?

我对自己有一些想法,但我会将它们作为答案发布,以便他们可以单独评论和投票.(我不确定这是不是这样做的方法,如果没有,我会删除我的答案并将其添加到问题中).



1> Pete Kirkham..:

我对Demeter法的后果的理解似乎与DrJokepu的不同 - 每当我将它应用于面向对象的代码时,它会导致更紧密的封装和内聚,而不是在程序代码中向合同路径添加额外的getter.

维基百科的规则为

更正式地说,函数的Demeter法则要求对象O的方法M只能调用以下类型的对象的方法:

    O本身

    M的参数

    在M中创建/实例化的任何对象

    O的直接组件对象

如果你有一个以'厨房'为参数的方法,Demeter说你不能检查厨房的组件,而不是你只能检查直接的组件.

写一堆函数只是为了满足这样的Demeter法则

Kitchen.GetCeilingColour()

看起来对我来说总是浪费时间,实际上是我完成工作的方式

如果Kitchen之外的方法通过厨房,严格的Demeter它也不能调用GetCeilingColour()的结果上的任何方法.

但无论哪种方式,重点是消除对结构的依赖,而不是将结构的表示从一系列链式方法移动到方法的名称.在Dog类中创建诸如MoveTheLeftHindLegForward()之类的方法对于实现Demeter没有任何作用.相反,请致电dog.walk()并让狗自己动手.

例如,如果要求发生变化并且我也需要天花板高度怎么办?

我会重构代码,以便您使用房间和天花板:

interface RoomVisitor {
  void visitFloor (Floor floor) ...
  void visitCeiling (Ceiling ceiling) ...
  void visitWall (Wall wall ...
}

interface Room { accept (RoomVisitor visitor) ; }

Kitchen.accept(RoomVisitor visitor) {
   visitor.visitCeiling(this.ceiling);
   ...
}

或者你可以通过将天花板的参数传递给visitCeiling方法来进一步消除吸气剂,但这通常会引入脆性耦合.

将它应用于医学示例,我希望SolubleAdminstrationRoute能够验证药物,或者至少调用药物的validateForSolubleAdministration方法,如果在药物类中封装了验证所需的信息.

但是,Demeter适用于OO系统 - 其中数据封装在对数据进行操作的对象中 - 而不是您正在讨论的系统,其具有不同的层,数据在哑层,可导航结构的层之间传递.我不认为Demeter必须像单片或基于消息的那样容易地应用于这样的系统.(在基于消息的系统中,您无法导航到不在消息克中的任何内容,因此无论您是否喜欢,您都会被Demeter困住)



2> Tamas Czineg..:

我知道我会被彻底毁灭,但我必须说我不喜欢得墨忒耳法.当然,像

dictionary["somekey"].headers[1].references[2]

真的很难看,但考虑一下:

Kitchen.Ceiling.Coulour

我没有反对这一点.写一堆函数只是为了满足这样的Demeter法则

Kitchen.GetCeilingColour()

看起来对我来说总是浪费时间,实际上是我完成工作的方式.例如,如果要求发生变化并且我也需要天花板高度怎么办?根据Demeter法则,我将不得不在Kitchen中编写另一个函数,这样我就可以直接获得Ceiling高度,最后我会在各处获得一些微小的getter函数,这些都是我认为非常混乱的东西.

编辑:让我重新说一下我的观点:

抽象事物的这种程度是否如此重要以至于我会花时间写3-4-5级的吸气剂/制定者?它真的能让维护更轻松吗?最终用户获得了什么吗?值得我花时间吗?我不这么认为.


主要论点是,您不应该在应用程序的其余部分泄漏Kitchen的实现.GUI真的需要知道天花板的颜色是如何存储的吗?或者GUI只是想要颜色?
@lassevk:感谢您的见解,我回答了第一句"我有点不喜欢".我以为我会解释Dementer法则的论点.根据应用程序的需要,我同意DrJokepu并使用Kitchen.Ceiling.Coulour.

3> kdgregory..:

Demeter违规的传统解决方案是"告诉,不要问".换句话说,根据您的状态,您应该告诉托管对象(您拥有的任何对象)采取某些操作 - 它将根据自己的状态决定是否按照您的要求执行操作.

举个简单的例子:我的代码使用了一个日志框架,我告诉我的记录器我想输出一个调试消息.然后,记录器根据其配置(可能未启用调试)决定是否实际将消息发送到其输出设备.在这种情况下,LoD违规将是我的对象询问记录器它是否会对消息做任何事情.通过这样做,我现在将我的代码与记录器内部状态的知识相结合(是的,我故意选择了这个例子).

但是,此示例的关键点是记录器实现了行为.

当我认为LOD的分解是表示对象打交道时的数据,与无行为.

在这种情况下,IMO遍历对象图与将XPath表达式应用于DOM没有什么不同.添加诸如"isThisMedicationWarranted()"之类的方法是一种更糟糕的方法,因为现在您在对象中分配业务规则,使其更难理解.

推荐阅读
帆侮听我悄悄说星星
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有