流利的接口是否违反Demeter定律?

时间:2020-03-05 18:54:41  来源:igfitidea点击:

维基百科上有关Demeter定律的文章说:

The law can be stated simply as "use only one dot".

但是,一个流畅接口的简单示例可能如下所示:

static void Main(string[] args)
{
   new ZRLabs.Yael.Pipeline("cat.jpg")
        .Rotate(90)
        .Watermark("Monkey")
        .RoundCorners(100, Color.Bisque)
        .Save("test.png");
}

那么,这在一起吗?

解决方案

回答

是的,尽管我们必须对这种情况采取一些务实的态度。我总是以《得墨meter耳法则》为准则,而不是规则。

当然,我们可能希望避免以下情况:

CurrentCustomer.Orders[0].Manufacturer.Address.Email(text);

也许替换为:

CurrentCustomer.Orders[0].EmailManufacturer(text);

随着越来越多的人使用ORM(通常将整个域表示为一个对象图),可能会为特定对象定义可接受的"范围"。也许我们应该采用demeter定律来建议我们不应将整个图形映射为可到达。

回答

好吧,法律的简短定义将其缩短得太多。真正的"法律"(实际上是有关API设计的建议)基本上说:仅访问我们自己创建的对象或者作为参数传递给对象。不要通过其他对象间接访问对象。流利的接口方法通常会返回对象本身,因此,如果我们再次使用该对象,它们不会违反法律。其他方法可以为我们创建对象,因此也不会违反。

另请注意,"法律"只是"经典" API的最佳做法建议。流利的接口是API设计的完全不同的方法,无法用Demeter定律进行评估。

回答

不必要。 "仅使用一个点"是对得墨meter耳定律的不准确总结。

当每个点代表不同对象的结果时,德米特耳定律不鼓励使用多个点,例如:

  • 第一个点是从ObjectA调用的方法,该方法返回ObjectB类型的对象
  • 下一个点是仅在ObjectB中可用的方法,它返回ObjectC类型的对象
  • 下一个点是仅在ObjectC中可用的属性
  • 广告无限

但是,至少在我看来,如果每个点的返回对象仍与原始调用者具有相同的类型,则不会违反Demeter法则:

var List<SomeObj> list = new List<SomeObj>();
//initialize data here
return list.FindAll( i => i == someValue ).Sort( i1, i2 => i2 > i1).ToArray();

在上面的示例中,FindAll()和Sort()都返回与原始列表相同类型的对象。没有违反《得墨Law耳法则》:该清单仅与其直系朋友交谈。

话虽这么说,只要它们返回与调用方相同的类型,并不是所有流畅的接口都违反Demeter法则。

回答

得墨meter耳定律的精神是,在给定对象引用或者类的情况下,应避免访问距离一个子属性或者方法不止一个的类的属性,因为这会紧密耦合这两个类,这可能是意料之外的,并且可能导致可维护性问题。

流利的接口是法律可接受的例外,因为它们的含义至少是紧密耦合的,因为所有的属性和方法都是组合在一起构成功能语句的迷你语言的术语。

回答

示例没有问题。毕竟,我们正在旋转,加水印等……始终是同一张图片。我相信我们一直在与Pipeline对象交谈,因此,只要代码仅取决于Pipeline的类,就不会违反LoD。

回答

1)完全没有违反它。

该代码等效于

var a = new ZRLabs.Yael.Pipeline("cat.jpg");
a = a.Rotate(90);
a = a.Watermark("Monkey");
a = a.RoundCorners(100, Color.Bisque);
a = a.Save("test.png");

2)就像奥尔·菲尔·哈克(Ol'Phil Haack)所说:得墨meter耳定律不是计算点数的练习