C#6.0的新空条件运算符是否违反Demeter定律?


29

德米特法律规定如下:

  • 每个单元对其他单元的知识应该有限:只有与当前单元“紧密”相关的单元。
  • 每个单位只能与朋友交谈;不要和陌生人说话。
  • 只与您的直系朋友交谈。

C#6.0引入了一个称为空条件运算符的新运算。恕我直言,它使编码更容易并提高了可读性。但这也使编写更多耦合的代码变得更容易,因为更容易在已经检查了无效性的类字段中导航(类似var x = A?.B?.C?.D?.E?.F?)。

陈述这名新操作员违反Demeter法则是否正确?


2
您为什么认为这A?.B?.C?.D?.E?.F?会违反它-LoD并不是关于多少个点,并且如果调用方法具有关于其结构的信息而不违反其点,则这样的调用将是完全可以接受的。仅仅说这样的代码可能违反LoD,还不能说它的所有使用违反了LoD。

14
读“ 得墨meter耳定律不是点算练习 ”吗?它讨论了这个确切的例子。
2015年

@outis:优秀的阅读。我并不是说每种形式的代码X.Y.Z.W.U都违反了“法律”。但是,以我在处理代码方面的经验来看,有90%的时间是纯丑陋的耦合代码。
亚瑟·里佐

2
@ArthurRizzo,但这不是空条件运算符违反LoD的问题。那是有错误的代码。操作员只是简化人类阅读的工具。在.?没有更多的违反毁灭之王比+-做。

1
RC Martin区分纯数据类和行为类。如果访问的属性公开了行为类的内部数据,则该片段肯定违反了LoD,但这与空条件运算符无关。无论如何,这些属性并不一定要公开内部数据,这可能是一种气味,但不会违反LoD。根据RC Martin所说,该模式对于纯数据类可能是绝对有效的。
Paul Kertscher

Answers:


44

陈述这名新操作员违反Demeter法则是否正确?

*


*空条件运算符是语言和.NET框架中的工具。 任何工具都有可能以可能损害给定应用程序可维护性的方式被滥用和使用。

但事实上,一个工具可以被滥用并不一定意味着它已经被滥用,也不该工具侵犯任何特定原则(一个或多个)可以举行。

得墨meter耳法则和其他都是有关如何编写代码的准则。它针对的是人类,而不是工具。因此,C#6.0语言中包含一个新工具这一事实并不一定会影响您应该如何编写和构建代码。

对于任何新的工具,你需要把它作为评价......如果谁最终维护你代码的家伙将是一个暴力精神病患者......。再次注意,这是对编写代码的人员的指导,而不是有关所用工具的指导。


foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1);会很好(并且不会比糟糕foo[0][0][0][0][0] = 1)……以及在其他情况下不会违反LoD的其他情况。

@MichaelT当您开始使用该维度的矩阵时,将索引视为向量/元组/数组本身似乎会变得更加容易,并且让矩阵类的内部成员担心数据的实际存储方式。(现在,考虑到这一点,至少在封装方面,它是Demeter定律的应用。)
JAB 2015年

(当然,这种做法可以更轻松地实现多维切片并拥有一些功能强大的矩阵工具。)
JAB 2015年

1
@JAB我只是想举一个例子。一个更好的选择可能是Dom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ...-它处理一些哑代码的结构问题。它不是一个陌生人...只是愚蠢的。在这种结构中,.?变得非常有用。

10

有点。

如果您只进行一次访问(a?.Foo),则等效于:

a == null ? null : a.Foo

大多数人都会同意这并不违反《得墨meter耳定律》。那时,只是语法糖可以提高可读性。

除此之外,还可能违反Demeter定律,并且此功能确实会促进这种用法。我什至会说,仅上述“好的”用法不足以保证对这种语言的这种更改,因此我希望这样做是为了支持不太清楚的好的用法。

也就是说,值得记住的是,《得墨meter耳法则》本身并不是法律,而是更多的指导原则。许多代码都违反了它,并且运行良好。有时,设计或代码的简单性所带来的价值超过违反Demeter法则所带来的风险。


除此之外,并不一定会破坏LoD,例如生成器模式
jk。

@Telastyn:我们正在谈论新的语言语法关于支持的方法调用:a?.Func1(x)?.Func2(y) 空合并运算符是别的东西。
Ben Voigt,2015年

@BenVoigt-啊,我刚读完这篇文章,这表明它仅适用于字段,属性和索引器。我没有方便使用的MSVS2015。你是对的。
Telastyn

1
a..Foo不是完全等于== null吗?空:a.Foo。前者只评估一次,后者则评估两次。如果a是迭代器,那可能很重要。
罗伦·佩希特尔

9

不。让我们既考虑运营商本身,又考虑您对其的严格使用。

.?A依赖于左值的类和方法返回的类型的知识量,也就是它本身.A != null。它需要知道该A属性是否存在,并返回可以与进行比较的值null

我们只能说,如果键入的属性确实违反了Demeter的定律。我们甚至没有被迫具有A具体类型(它的值可以是派生类型)。这里的耦合很小。

现在让我们考虑一下var x = A?.B?.C?.D?.E?.F

这意味着该A类型必须为可以为null的类型,或者可以具有B属性,必须为可以为null的类型或具有C属性,依此类推,直到该E属性的类型可以为null或可能F有财产。

换句话说,我们需要使用静态类型的语言来执行此操作,或者对类型松散的情况下可以返回的类型施加约束。C#在大多数情况下使用静态类型,因此我们没有做任何更改。

如果有的话,以下代码也将违反法律:

ExplicitType x;
var b = A.B;
if (b == null)
  x = null;
else
{
  var c = b.C;
  if (c == null)
    x = null;
  else
  {
    var d = c.D;
    if (d == null)
      x = null;
    else
    {
      var e = d.E;
      if (e == null)
        x = null;
      else
        x = e.F;
    }
  }
}

哪个完全一样。此代码使用不同元素的耦合,需要“知道”整个耦合链,但是它使用的代码并没有违反Demeter定律,每个单元都具有明确定义的耦合下一个。


3
+1新的运算符仅仅是您所描述的苦味配方的语法糖。
罗斯·帕特森

1
好吧,如果开发人员编写的代码看起来像这样,我认为更容易注意到某些错误。我知道运算符是100%的合成糖,但我仍然认为,var x = A?.B?.C?.D?.E?.F即使最终相同,人们也会比所有if / eles更加喜欢编写类似的东西。
亚瑟·里佐

2
容易发现不正确的地方,A?.B?.C?.D?.E?.F因为少了什么是错的。要么我们应该尝试F通过该路径,否则我们不应该这样做,而较长的表单可能会出现错误以及它不是正确的做法的错误。
乔恩·汉纳

@ArthurRizzo但是,如果您将上述类型的代码与LoD违例相关联,那么在不需要空检查的情况下很容易错过它们,您可以这样做A.B.C.D。要查找一件东西(链接的属性访问)要简单得多,而不是依赖于一个不相关的细节(空检查)的两个不同的事情要简单得多
Ben Aaronson

5

可以出于封装行为或保存数据的目的而创建对象,并且可以出于与外部代码共享或由其创建者私有保存的目的而创建对象。

为封装行为(无论是否共享)或与外部代码共享而创建的对象(无论它们封装行为还是数据)通常应通过其表面接口访问。但是,当创建数据保留对象以供其创建者专用时,则不适用避免“深度”访问的通常的《 Demeter of Demeter》理由。如果以需要调整其他代码的方式更改了在对象中存储或操作数据的类的一部分,则可以保证所有此类代码都得到更新,因为(如上所述),对象是为创建对象而创建的。一类的专用。

虽然我认为?操作员可能设计得更好,在很多情况下对象使用嵌套数据结构,操作员有很多用例,而这些用例不会违反Demeter法则所表达的原理。它可能被用来违反LoD的事实不应被视为反对运营商的论据,因为它并不比“。”更糟糕。运营商在这方面。


为什么《得墨meter耳定律》不适用于数据保存对象?
Telastyn

2
@Telastyn:LoD的目的是避免一段代码访问其他对象可能会操纵或关心的内部对象时出现的问题。如果宇宙中没有别的东西可以操纵或关心内部物体的状态,那么就无需防范此类问题。
2015年

我不确定我是否同意。并不是说其他​​事情可能需要修改数据,而是要通过一条路径耦合到包含的对象(本质上是耦合的三个点-两个对象及其关系)。有时候,这种耦合并不是什么大问题,但对我来说似乎仍然很臭。
Telastyn

@Telastyn:关于路径的观点很不错,但是我认为我的观点很好。通过多个路径访问对象会在这些路径之间建立耦合。如果某些访问是通过浅路径进行的,那么通过深路径进行访问也可能会导致不必要的耦合。但是,如果所有访问都是通过一条特定的深层路径进行的,那么将没有任何东西可以耦合到该深层路径。
2015年

@Telastyn遍历数据结构以深入了解数据是完全可以的。它与嵌套方法调用不同。你有时需要知道一个数据结构,它是如何嵌套的,同样不会去像一个服务和它自己的嵌套服务/存储库等对象
每Hornshøj-Schierbeck
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.