应用具有立面图案的DEMETER法则

时间:2014-07-15 23:07:40

标签: design-patterns law-of-demeter

在敏捷开发人员的基本技能方面,在需求与能力界面,第12章,我试图理解提出的主要解决方案,以应用作者提到的最终提出的挑战本章

简化故事。

我们从以下研究案例开始:

public class City {
  public string name{};
  public City twinCity{};
  public Street[] streets{};
}
public class Street {
  public string name{};
  public House[] houses{};
}
public class House {
  public int number{};
  public Color color{};
}

Model of a City Grid

作者声明:

  

这样的模型鼓励我们揭露而不是封装。如果   您的代码具有对特定City实例的引用,比如说   映射西雅图,你想要1374 Main的房子颜色   街道,那么你可能会做类似以下的事情:

public Foo() {
  Color c = Seattle.streets()["Main"].
    houses()[1374].
    color();
}
  

问题,如果这是作为一般做法,是系统   在任何地方发展依赖关系,并改变其中的任何部分   模型可以在这些依赖关系的链中上下起作用。   这就是德米特法则,其中指出“不要与之交谈   陌生人,“进来。这在对象系统中被形式化为法律   用于函数/方法的Demeter。对象O的方法M可以仅   调用以下类型的对象的方法:

     
      
  1. 0的
  2.   
  3. M的参数
  4.   
  5. 在M
  6. 中实例化的任何对象   
  7. O的直接组件对象
  8.   
  9. O
  10. 可访问的任何全局变量   

并建议在应用DEMTER法则时,我们应该瞄准像

这样的东西
public Foo() {
   Color c = Seattle.ColorOfHouseInStreet("Main",1374);
}

并迅速警告以下内容:

  

虽然这最初似乎是一个明智的政策,但它可以很快   失控,因为任何给定实体的界面都可以预期   提供字面上与之相关的任何内容。这些接口倾向于   随着时间的推移膨胀,事实上似乎几乎没有尽头   特定玻璃可能最终支持的公共方法的数量。

然后在快速绕道解释耦合和依赖的问题后,他提到了通过服务接口分离客户端及其服务的重要性,并且可能进一步分离客户端"需要界面& #34;来自"服务能力界面"通过使用适配器作为理想但不一定实用的东西;

Ideal decoupling

他建议为了解决这个问题,我们可以将DEMETER的法则与需求/能力分离相结合,使用外观模式解释如下:

来自原始依赖

Violation of the law of demeter

在应用demeter定律和需求/能力界面分离时,我们应该首先得到:

Too complex

但鉴于从嘲讽的角度来看这是不切实际的,我们可以通过以下方式获得更简单的东西:

LoD needs/Capability separation Mock

问题在于我不知道这究竟是如何解决不违反得墨忒耳法的问题。我认为它维护原始客户和原始服务之间的法律。但它只是在FACADE中移动了违规行为。

3 个答案:

答案 0 :(得分:3)

我说,根据你的描述,他只是以CityMapFacade的形式在Cirty / Street / House功能周围引入了一个黑盒子组件。此外,该组件以接口IClientNeeds的形式粘附到面向公众的界面。由于城市/街道/房屋关系的复杂性未被暴露,因此这种黑盒封装可以保护并使可测试的更大的应用程序。看起来曾经是旧的又是新的:)这种黑盒子组件风格的方法(缺乏一个更好的术语)已经存在了很长时间。它侧重于使用定义良好的界面创建可重用,封装的功能组件。只要面向公众的合同得到满足,客户就不会关心实施。该组件是可更换的,可升级的,可模拟的。在组件内,复杂性也会降低,因为它只负责应用程序的隔离子集。它通常是完全可单元测试的,以确保它正确实现其公共合同/接口。在我看来,作者已经在地毯下进行了更深入的讨论,以简化他的解释。

答案 1 :(得分:3)

我猜这是" 误传"在你和作者之间。

第1步:你有这个:

needs interfaces

我希望你看到此时没有LoD违规:每个具体的类依赖于它的需求的抽象,因此只调用那个有限的接口;加上对实体的依赖(例如City的{​​{1}}),这是LoD允许的。所以,到目前为止还没有违规行为。


第2步:然后你有了这个:

needs facade

我认为这里的作者意味着CityCapabilityAdapter依赖于所有3个实体概念,但实际上并不直接使用它们。相反,它通过LoD工具的方式与它们一起工作。例如外观可能取决于之前已经介绍过的适配器。所以,再次,这里没有LoD违规。

为了进一步阐明这种解释,请注意图中的抽象事物,它们是斜体(UML 2.0)。因此,CityMapFacade是一个接口,它负责解决自己的LoD问题。这些可以通过已经证明的事实(例如适配器)来解决。图中没有显示具体的解决方案 - 它只是谈论抽象。并且没有机会在立面的具体实现中争论LoD违规,因为作者已经证明他有通过引入适配器来解决违规问题的工具。

答案 2 :(得分:2)

我不知道你读过的那本书,当我第一次读你的问题时,我认为那是" LoD疯了#34;我理解你在没有违反LoD的情况下询问这个Facade的具体实现。

让我首先想到的是: 如果我需要知道财产"颜色"一个对象" House",我认为绝对可以向对象House询问其财产。 当然,我们不需要讨论从城市到街道走向同时连锁的一个非常糟糕的主意,因为你得到了依赖。

我只是将方法getHouse实现为

public class City {
  public House getHouse(street, number) {...}
}

该方法可能只是发现已解决的街道对象并询问有关不会违反LoD的给定数字的房屋。

但当然你会得到以下代码:

public Foo() {
  Color c = Seattle.getHouse("Main", 1374).color();
}

如果我们从字面上理解它再次违反了LoD。

但无论如何,这是我实施它的方式,如果那是我需要房子颜色的唯一点,因为我没有看到任何好处 为单次使用创建ServiceFacades。 但是如果需要不止一次并且你真的不想破坏LoD那么这很容易,但是我需要第二个视图才能看到它。 Facade的实现看起来如下:

public class CityMapService
{
  public Color getColorOfHouse(City city, String nameOfStreet, int number)
  {
    Street street = city.getStreet(nameOfStreet);
    return getColorOfHouse(street, number);
  }

  public Color getColorOfHouse(Street street, int number)
  {
    House house = street.getHouse(number);
    return getColorOf(house);
  }

  public Color getColorOf(House house)
  {
    return house.getColor();
  }
}

没有方法会破坏LoD。

这样做有意义吗?我会说是的。每个物体的内部结构都没有暴露出来,并且“更大”。城市,街道和房屋如何连接的结构隐藏在您的立面后面。如果任何细节发生变化,您很可能只需更改一个方法。 嘲笑仍然非常容易和直接。