Verstößt der neue nullbedingte Operator von C # 6.0 gegen das Gesetz von Demeter?

29

Das Gesetz von Demeter besagt Folgendes:

  • Jede Einheit sollte nur begrenzte Kenntnisse über andere Einheiten haben: nur Einheiten, die "eng" mit der aktuellen Einheit verbunden sind.
  • Jede Einheit sollte nur mit ihren Freunden sprechen. Sprich nicht mit Fremden.
  • Sprechen Sie nur mit Ihren direkten Freunden.

In C # 6.0 wurde ein neuer Operator eingeführt, der als nullbedingter Operator bezeichnet wird . IMHO erleichtert es die Codierung und verbessert die Lesbarkeit. Aber es macht es auch einfacher, mehr gekoppelten Code zu schreiben, da es einfacher ist, durch Klassenfelder zu navigieren, die bereits auf NULL prüfen (so etwas wie var x = A?.B?.C?.D?.E?.F?).

Stimmt es, dass dieser neue Betreiber gegen das Gesetz von Demeter verstößt?

Arthur Rizzo
quelle
2
Warum glaubst du, dass A?.B?.C?.D?.E?.F?dies gegen die Regeln verstoßen würde? Bei LoD geht es nicht darum, wie viele Punkte es gibt, und wenn die aufrufende Methode solche Informationen über die Struktur enthält, die nicht gegen ihre Punkte verstößt , wäre ein solcher Aufruf durchaus akzeptabel. Dass ein solcher Code könnte LoD verletzen ist nicht genug , um zu sagen , dass alle Verwendungen es tun verletzen LoD.
14
Lesen Sie " Das Gesetz von Demeter ist keine Punktzählübung "? Es wird genau dieses Beispiel besprochen.
outis
@outis: sehr gut gelesen. Ich sage nicht, dass jeder Code in Form von X.Y.Z.W.Ugegen das "Gesetz" verstößt. Aber meiner Erfahrung nach ist Code in 90% der Fälle einfach nur hässlich gekoppelter Code.
Arthur Rizzo
2
@ArthurRizzo, aber das ist kein Problem, wenn der nullbedingte Operator gegen LoD geht. Das ist der Code, der Schuld ist. Der Bediener ist nur ein Hilfsmittel, um das Ablesen zu vereinfachen. Das .?verstößt nicht mehr gegen das LoD als +oder -.
1
RC Martin unterscheidet zwischen reinen Datenklassen und Verhaltensklassen. Wenn die Eigenschaften, auf die zugegriffen wird, interne Daten einer Verhaltensklasse verfügbar machen, verstößt das Snippet zwar gegen die LoD, dies hat jedoch nichts mit dem nullbedingten Operator zu tun. Auf jeden Fall sind die Eigenschaften nicht verpflichtet, interne Daten freizulegen, die möglicherweise riechen, aber nicht die LoD verletzen. Laut RC Martin kann das Schema mit reinen Datenklassen absolut gültig sein.
Paul Kertscher

Antworten:

44

Stimmt es, dass dieser neue Betreiber gegen das Gesetz von Demeter verstößt?

Nein *


* Der Null-Bedingungsoperator ist ein Tool innerhalb der Sprache und des .NET-Frameworks. Jedes Tool kann auf eine Weise missbraucht und verwendet werden, die die Wartbarkeit einer bestimmten Anwendung beeinträchtigen kann.

Aber die Tatsache , dass ein Werkzeug kann missbraucht werden , bedeutet nicht zwangsläufig , dass es hat missbraucht werden , um, noch , dass das Werkzeug gegen ein bestimmtes Prinzip (n) , die gehalten werden können.

Das Gesetz von Demeter und andere sind Richtlinien, wie Sie Ihren Code schreiben sollten. Es zielt auf den Menschen ab, nicht auf die Werkzeuge. Die Tatsache, dass die C # 6.0-Sprache ein neues Tool enthält, wirkt sich nicht unbedingt darauf aus, wie Sie Ihren Code schreiben und strukturieren sollten.

Bei jedem neuen Tool muss bewertet werden, ob der Typ, der den Code verwaltet, ein gewalttätiger Psychopath ist . Beachten Sie erneut, dass dies eine Anleitung für die Person ist, die den Code schreibt, und nicht für die verwendeten Tools.

Gemeinschaft
quelle
foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1);wäre in Ordnung (und nicht schlimmer als foo[0][0][0][0][0] = 1) ... und viele andere Situationen, in denen dies nicht gegen die LoD verstößt.
@MichaelT Wenn Sie sich mit Matrizen dieser Dimension befassen, wird es anscheinend einfacher, die Indizes als Vektor / Tupel / Array selbst zu behandeln und die Interna der Matrixklasse sich Gedanken darüber zu machen, wie die Daten tatsächlich gespeichert werden. (Was, jetzt wo ich darüber nachdenke, eine Anwendung des Gesetzes von Demeter ist, zumindest was die Einkapselung betrifft.)
JAB
(Und diese Art von Übung macht es natürlich einfacher, mehrdimensionales Schneiden zu implementieren und einige wirklich leistungsstarke Matrixwerkzeuge zu haben.)
JAB
1
@JAB Ich habe nur versucht, ein Beispiel zu finden. Ein besseres wäre wahrscheinlich Dom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ...- es ist ein Problem der Verarbeitung der Struktur von etwas dummem Code. Es ist kein Fremder ... es ist einfach dumm. Das .?wird in solchen Strukturen sehr nützlich.
10

Art von.

Wenn Sie nur einen Zugriff ( a?.Foo) ausführen, entspricht dies:

a == null ? null : a.Foo

was die meisten Leute zustimmen würden, ist kein Verstoß gegen das Gesetz von Demeter. Zu diesem Zeitpunkt ist es nur syntaktischer Zucker, um die Lesbarkeit zu verbessern.

Mehr als das, und es würde wahrscheinlich das Gesetz von Demeter verletzen, und diese Funktion fördert diese Art der Verwendung. Ich würde sogar sagen, dass die oben genannte "gute" Verwendung allein nicht ausreicht, um diese Art der Änderung der Sprache zu rechtfertigen. Ich gehe daher davon aus, dass sie die weniger klar gute Verwendung unterstützt.

Das Gesetz von Demeter ist jedoch kein Gesetz an sich, sondern eher eine Richtlinie. Viele Codes verstoßen dagegen und funktionieren gut. Manchmal ist die Einfachheit des Designs oder des Codes mehr wert als das Risiko, das durch die Verletzung des Gesetzes von Demeter entsteht.

Telastyn
quelle
alles andere unterbricht nicht unbedingt LoD, z. B. Builder-Pattern
jk.
@Telastyn: Die neue Sprachsyntax, über die wir sprechen, unterstützt Methodenaufrufe: a?.Func1(x)?.Func2(y) Der Null-Koaleszenzoperator ist etwas anderes.
Ben Voigt
@BenVoigt - ah, ich habe den Artikel verlassen, der angab, dass er nur mit Feldern, Eigenschaften und Indexern funktioniert. Ich hatte MSVS2015 nicht zum Testen zur Hand. Du hast recht.
Telastyn
1
a? .Foo ist nicht ganz gleich a == null? null: a.Foo. Ersteres wertet nur einmal aus, letzteres zweimal. Das könnte von Bedeutung sein, wenn a ein Iterator wäre.
Loren Pechtel
9

Betrachten wir sowohl den Operator als auch die stark verkettete Verwendung, die Sie dafür haben.

Dies .?Ahängt von der Kenntnis der Klasse ab, bei der es sich um den Wert left handelt, und von dem Typ, der von der Methode zurückgegeben wird, wie dies auch der .A != nullFall ist. Es muss bekannt sein, dass die AEigenschaft vorhanden ist und einen vergleichbaren Wert zurückgibt null.

Wir können nur argumentieren, dass dies gegen das Gesetz von Demeter verstößt, wenn typisierte Eigenschaften dies tun. Wir sind nicht einmal gezwungen, Aeinen konkreten Typ zu haben (sein Wert könnte von einem abgeleiteten Typ sein). Die Kopplung hier ist minimal.

Betrachten wir nun var x = A?.B?.C?.D?.E?.F.

Dies bedeutet, Adass der Typ null sein muss oder eine BEigenschaft haben muss, der Typ null sein muss oder eine CEigenschaft haben muss, und so weiter, bis der Typ der EEigenschaft null sein kann oder könnte eine FEigenschaft haben.

Mit anderen Worten, wir müssen dies entweder mit einer statisch typisierten Sprache tun oder eine Einschränkung für die Typen anwenden, die zurückgegeben werden können, wenn die Typisierung locker ist. C # verwendet in den meisten Fällen statische Typisierung, sodass wir nichts geändert haben.

Wenn wir das hätten, würde der folgende Code ebenfalls gegen das Gesetz verstoßen:

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;
    }
  }
}

Welches ist genau das gleiche . Dieser Code, der ist mit der Kopplung verschiedenen Elementen Bedürfnisse zu „weiß“ über die gesamte Kette der Kupplung, aber es ist Code, der so nicht das Gesetz von Demeter verstoßen zu tun, mit jeder Einheit mit einer gut definierten Kopplung mit der nächste.

Jon Hanna
quelle
3
+1 der neue Operator ist nur syntaktischer Zucker für das bittere Rezept, das Sie beschrieben haben.
Ross Patterson
1
Nun, wenn ein Entwickler einen Code schreibt, der so aussieht, ist es meiner Meinung nach einfacher zu bemerken, dass etwas möglicherweise nicht stimmt. Ich weiß, dass der Operator zu 100% aus syntaktischem Zucker besteht, aber ich glaube, dass die Leute es tendenziell einfacher haben, so etwas zu schreiben var x = A?.B?.C?.D?.E?.Fals alle anderen, auch wenn sie am Ende die gleichen sind.
Arthur Rizzo
2
Es ist einfacher zu bemerken, dass etwas nicht stimmt, A?.B?.C?.D?.E?.Fweil weniger falsch sein kann. Entweder sollten wir versuchen, Füber diesen Pfad zu gelangen , oder wir sollten nicht, während die längere Form Fehler enthalten könnte, und der Fehler, dass es nicht das Richtige ist, was zu tun ist.
Jon Hanna
@ArthurRizzo Aber wenn Sie die oben genannten Code-Typen mit LOD-Verstößen in Verbindung bringen, können Sie diese leicht übersehen, wenn keine Nullprüfung erforderlich ist und Sie dies einfach tun können A.B.C.D. Es ist viel einfacher, auf eine einzige Sache zu achten (Zugriff auf verkettete Eigenschaften), als auf zwei verschiedene Dinge, die von einem ziemlich irrelevanten Detail abhängen (Nullprüfung)
Ben Aaronson
5

Das Objekt kann erstellt werden, um Verhalten zu kapseln oder Daten zu speichern, und Objekte können erstellt werden, um mit externem Code geteilt zu werden oder um privat von ihrem Ersteller gehalten zu werden.

Auf Objekte, die zum Zwecke der Einkapselung von Verhalten (unabhängig davon, ob sie gemeinsam genutzt werden oder nicht) oder zur gemeinsamen Nutzung mit externem Code (unabhängig davon, ob sie Verhalten oder Daten einkapseln) erstellt wurden, sollte im Allgemeinen über ihre Oberflächenschnittstelle zugegriffen werden. Bei der Erstellung von Datenobjekten zur ausschließlichen Verwendung durch ihren Ersteller gelten jedoch nicht die normalen Law-of-Demeter-Gründe zur Vermeidung eines "tiefen" Zugriffs. Wenn ein Teil einer Klasse, in der Daten im Objekt gespeichert oder bearbeitet werden, so geändert wird, dass ein anderer Code angepasst werden muss, kann garantiert werden, dass der gesamte Code aktualisiert wird, da das Objekt - wie oben erwähnt - für das Objekt erstellt wurde die ausschließliche Verwendung einer Klasse.

Während ich das denke? Der Operator hätte vielleicht besser entworfen werden können. Es gibt genügend Situationen, in denen Objekte verschachtelte Datenstrukturen verwenden, so dass der Operator viele Anwendungsfälle hat, die nicht gegen die Prinzipien des Gesetzes von Demeter verstoßen würden. Die Tatsache, dass es zur Verletzung der LoD verwendet werden könnte, sollte nicht als Argument gegen den Operator gewertet werden, da es nicht schlechter ist als das "." Betreiber in dieser Hinsicht.

Superkatze
quelle
Warum gilt das Demeter-Gesetz nicht für Objekte, die Daten enthalten?
Telastyn
2
@Telastyn: Der Zweck der LoD besteht darin, Probleme zu vermeiden, die auftreten können, wenn ein Teil des Codes auf innere Objekte zugreift, die von anderen manipuliert oder gepflegt werden könnten . Wenn nichts anderes im Universum möglicherweise den Zustand der inneren Objekte manipulieren oder sich darum kümmern könnte, dann besteht keine Notwendigkeit, sich vor solchen Problemen zu schützen.
Supercat
Ich bin mir nicht sicher, ob ich damit einverstanden bin. Es ist nicht so, dass andere Dinge die Daten ändern könnten, sondern dass Sie über einen Pfad (im Wesentlichen drei Kopplungspunkte - die beiden Objekte und ihre Beziehung) mit dem enthaltenen Objekt koppeln. Manchmal ist diese Kopplung keine große Sache, aber sie scheint mir trotzdem stinkend.
Telastyn
@ Telastyn: Dein Standpunkt zu Pfaden ist gut, aber ich denke, mein Standpunkt ist in Ordnung. Durch den Zugriff auf ein Objekt über mehrere Pfade wird eine Kopplung zwischen diesen Pfaden hergestellt. Wenn ein Teil des Zugriffs über einen flachen Pfad erfolgt, kann auch der Zugriff über einen tiefen Pfad zu unerwünschter Kopplung führen. Wenn der gesamte Zugriff jedoch über einen bestimmten tiefen Pfad erfolgt, ist für diesen tiefen Pfad nichts zu koppeln.
Supercat
@Telastyn Es ist vollkommen in Ordnung, eine Datenstruktur zu durchlaufen, um tief in die Daten zu gelangen. Dies ist nicht dasselbe wie bei verschachtelten Methodenaufrufen. Manchmal müssen Sie eine Datenstruktur kennen und wissen, wie sie verschachtelt ist. Dasselbe gilt nicht für Objekte wie einen Dienst und seine eigenen verschachtelten Dienste / Repositorys usw.
Per Hornshøj-Schierbeck