Le nouvel opérateur null-conditionnel de C # 6.0 va-t-il à l'encontre de la loi de Demeter?

29

La loi de Déméter stipule ce qui suit:

  • Chaque unité ne devrait avoir qu'une connaissance limitée des autres unités: seules les unités "étroitement" liées à l'unité actuelle.
  • Chaque unité ne devrait parler qu'à ses amis; ne parlez pas à des étrangers.
  • Ne parlez qu'à vos amis immédiats.

C # 6.0 a introduit un nouvel opérateur appelé opérateur conditionnel nul . À mon humble avis, il facilite le codage et améliore la lisibilité. Mais cela facilite également l'écriture de code couplé, car il est plus facile de parcourir les champs de classe, en vérifiant déjà la nullité (quelque chose comme var x = A?.B?.C?.D?.E?.F?).

Est-il exact de dire que ce nouvel opérateur va à l'encontre de la loi de Déméter?

Arthur Rizzo
la source
2
Pourquoi pensez-vous que A?.B?.C?.D?.E?.F?cela le violerait - LoD ne concerne pas le nombre de points et si la méthode appelante possède de telles informations sur la structure qui ne sont pas en violation avec ses points, un tel appel serait parfaitement acceptable. Qu'un tel code puisse violer LoD ne suffit pas à dire que toutes les utilisations de celui -ci violent LoD.
14
Lire " La loi de Déméter n'est pas un exercice de comptage de points "? Il discute de cet exemple exact.
2015
@outis: excellente lecture. Je ne dis pas que chaque code sous forme de X.Y.Z.W.Uest une violation de la "loi". Mais, dans mon expérience avec le code, 90% du temps, il s'agit simplement de code couplé laid.
Arthur Rizzo
2
@ArthurRizzo mais ce n'est pas un problème avec l'opérateur conditionnel nul allant contre LoD. C'est le code qui est en faute. L'opérateur n'est qu'un outil pour simplifier la lecture humaine. Le .?plus LoD que constitue une violation +ou -fait.
1
RC Martin fait la distinction entre les classes de données pures et les classes comportementales. Si les propriétés accédées exposent des données internes d'une classe comportementale, l'extrait viole certainement le LoD, mais cela n'a rien à voir avec l'opérateur conditionnel nul. Quoi qu'il en soit, les propriétés ne sont pas tenues d'exposer des données internes, ce qui pourrait être une odeur, mais ne violent pas le LoD. Selon RC Martin, le schéma peut être absolument valide avec des classes de données pures.
Paul Kertscher

Réponses:

44

Est-il exact de dire que ce nouvel opérateur va à l'encontre de la loi de Déméter?

Non *


* L'opérateur conditionnel nul est un outil du langage et du framework .NET. Tout outil peut être utilisé de manière abusive et utilisée de manière à nuire à la maintenabilité d'une application donnée.

Mais le fait qu'un outil puisse être maltraité ne signifie pas nécessairement qu'il doit être maltraité, ni que l'outil viole un ou des principes particuliers pouvant être détenus.

La loi de Demeter et d'autres sont des directives sur la façon dont vous devez écrire votre code. Il est destiné aux humains, pas aux outils. Ainsi, le fait que le langage C # 6.0 dispose d'un nouvel outil n'affecte pas nécessairement la façon dont vous devez écrire et structurer votre code.

Avec tout nouvel outil, vous devez évaluer comme ... si le gars qui finit par le maintien de votre code sera un psychopathe violent ... . Notez à nouveau qu'il s'agit d'un guide pour la personne qui écrit le code et non des outils utilisés.

Communauté
la source
foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1);serait bien (et pas pire que foo[0][0][0][0][0] = 1) ... et beaucoup d'autres situations où cela ne viole pas LoD.
@MichaelT Lorsque vous commencez à entrer dans les matrices de cette dimensionnalité, il semble qu'il deviendrait plus facile de traiter les indices comme un vecteur / tuple / tableau lui-même et de laisser les internes de la classe de matrice se soucier de la façon dont les données sont réellement stockées. (Ce qui, maintenant que j'y pense, est une application de la loi de Demeter, au moins en ce qui concerne l'encapsulation.)
JAB
(Et, bien sûr, ce genre de pratique facilite l'implémentation du découpage multidimensionnel et la présence d'outils de matrice vraiment puissants.)
JAB
1
@JAB J'essayais juste de trouver un exemple. Un meilleur serait probablement Dom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ...- c'est une question de traitement de la structure d'un code stupide. Ce n'est pas un étranger ... c'est juste stupide. Le .?devient très utile dans de telles structures.
10

Sorte de.

Si vous ne faites qu'un seul accès ( a?.Foo), cela équivaut à:

a == null ? null : a.Foo

que la plupart des gens conviendraient ne constitue pas une violation de la loi de Déméter. À ce stade, il ne s'agit que de sucre syntaxique pour améliorer la lisibilité.

Rien de plus que cela, et cela violerait probablement la loi de Demeter, et cette fonctionnalité a tendance à promouvoir ce type d'utilisation. Je dirais même que le "bon" usage ci-dessus ne suffit pas à lui seul à justifier ce type de changement de langage, donc je m'attends à ce qu'il soit fait pour supporter le moins clairement bon usage.

Cela dit, il convient de rappeler que la loi de Déméter n'est pas une loi en soi, mais plutôt une ligne directrice. Beaucoup de code le viole et fonctionne bien. Parfois, la simplicité de conception ou de code vaut plus que le risque posé par la violation de la loi de Déméter.

Telastyn
la source
rien de plus que cela ne casse pas nécessairement LoD, par exemple le modèle de générateur
jk.
@Telastyn: La nouvelle syntaxe du langage dont nous parlons prend en charge les appels de méthode: a?.Func1(x)?.Func2(y) L'opérateur de coalescence nul est autre chose.
Ben Voigt
@BenVoigt - ah, je sortais de l'article, qui indiquait que cela ne fonctionnait qu'avec les champs, les propriétés et les indexeurs. Je n'avais pas MSVS2015 à portée de main pour tester. Vous avez raison.
Telastyn
1
un? .Foo n'est pas tout à fait équivalent à un == null? null: a.Foo. Le premier l'évalue une seule fois, le second l'évalue deux fois. Cela pourrait être important si a était un itérateur.
Loren Pechtel
9

Considérons à la fois l'opérateur seul et l'utilisation fortement enchaînée que vous en avez.

En soi, cela .?Adépend de la même quantité de connaissance de la classe que la valeur de gauche est et du type retourné par la méthode comme le .A != nullfait, à savoir. Il doit savoir que la Apropriété existe et renvoie une valeur qui peut être comparée null.

Nous ne pouvons affirmer que cela viole la loi de Demeter que si les propriétés typées le font. Nous ne sommes même pas obligés d'avoir Aun type concret (sa valeur pourrait être de type dérivé). Le couplage ici est minime.

Voyons maintenant var x = A?.B?.C?.D?.E?.F.

Ce qui signifie que cela Adoit être d'un type qui pourrait être nul, ou pourrait avoir une Bpropriété, qui doit être d'un type qui pourrait être nul ou avoir une Cpropriété, et ainsi de suite jusqu'à ce que le type de la Epropriété soit quelque chose qui pourrait être nul ou pourrait avoir une Fpropriété.

En d'autres termes, nous devons le faire avec un langage à typage statique ou avoir appliqué une contrainte sur les types qui peuvent être renvoyés si le typage est lâche. Dans la plupart des cas, C # utilise le typage statique, nous n'avons donc rien changé.

Si nous avions alors le code suivant violerait également la loi:

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

C'est exactement pareil . Ce code qui utilise le couplage de différents éléments doit "connaître" la chaîne complète de couplage, mais il utilise un code qui ne viole pas la loi de Déméter pour ce faire, chaque unité ayant un couplage bien défini avec le suivant.

Jon Hanna
la source
3
+1 le nouvel opérateur est simplement du sucre syntaxique pour la recette amère que vous avez décrite.
Ross Patterson,
1
Eh bien, si un développeur écrit un code qui ressemble à ça, je pense qu'il est plus facile de remarquer que quelque chose ne va pas. Je sais que l'opérateur est 100% sucre syntaxique, mais quand même, je pense que les gens ont tendance à être plus à l'aise pour écrire quelque chose comme var x = A?.B?.C?.D?.E?.Ftous ces if / elses même s'ils sont finalement les mêmes.
Arthur Rizzo du
2
Il est plus facile de remarquer que quelque chose ne va pas, A?.B?.C?.D?.E?.Fcar il y en a moins qui peut être mauvais; soit nous devrions essayer de passer Fpar ce chemin, soit nous ne devrions pas, alors que le formulaire plus long pourrait contenir des erreurs ainsi que l'erreur de ne pas être la bonne chose à faire.
Jon Hanna
@ArthurRizzo Mais si vous associez le type de code ci-dessus à des violations de LoD, il est facile de les manquer dans le cas où aucune vérification nulle n'est nécessaire et vous pouvez simplement le faire A.B.C.D. Il est beaucoup plus simple d'avoir une seule chose à rechercher (accès à la propriété chaînée) plutôt que deux choses différentes qui dépendent d'un détail assez peu pertinent (vérification nulle)
Ben Aaronson
5

L'objet peut être créé dans le but d'encapsuler des comportements ou de conserver des données, et des objets peuvent être créés dans le but d'être partagés avec du code extérieur ou détenus en privé par leur créateur.

Les objets créés dans le but d'encapsuler un comportement (qu'il soit partagé ou non), ou pour être partagés avec du code extérieur (qu'ils encapsulent un comportement ou des données) doivent généralement être accessibles via leur interface de surface. Cependant, lorsque des objets contenant des données sont créés pour être utilisés exclusivement par leur créateur, les raisons normales de la loi de Demeter pour éviter un accès "profond" ne s'appliquent pas. Si une partie d'une classe qui stocke ou manipule des données dans l'objet est modifiée d'une manière qui nécessiterait l'ajustement d'un autre code, il sera possible de garantir que tout ce code est mis à jour car - comme indiqué ci-dessus - l'objet a été créé pour l'utilisation exclusive d'une classe.

Alors que je pense que le?. L'opérateur aurait peut-être pu être mieux conçu, il y a suffisamment de situations où les objets utilisent des structures de données imbriquées que l'opérateur a de nombreux cas d'utilisation qui ne violeraient pas les principes exprimés par la loi de Demeter. Le fait qu'il puisse être utilisé pour violer la LoD ne doit pas être considéré comme un argument contre l'opérateur, car il n'est pas pire que le "." opérateur à cet égard.

supercat
la source
Pourquoi la loi de Déméter ne s'applique-t-elle pas aux objets contenant des données?
Telastyn
2
@Telastyn: Le but de la LoD est d'éviter les problèmes qui peuvent survenir si un morceau de code accède à des objets internes que quelque chose d'autre pourrait manipuler ou prendre en compte . Si rien d'autre dans l'univers ne peut manipuler ou se soucier de l'état des objets intérieurs, il n'est pas nécessaire de se prémunir contre de tels problèmes.
supercat
Je ne suis pas sûr d'être d'accord. Ce n'est pas que d'autres choses puissent se soucier de modifier les données, c'est que vous vous connectez à l'objet contenu via un chemin (essentiellement trois points de couplage - les deux objets et leur relation). Parfois, ce couplage ne va pas être un gros problème, mais il me semble toujours malodorant.
Telastyn
@Telastyn: Votre point sur les chemins est bon, mais je pense que mon point tient très bien. L'accès à un objet via plusieurs chemins crée un couplage entre ces chemins. Si certains accès se font par un chemin peu profond, l'accès par un chemin profond peut également provoquer un couplage indésirable. Si tous les accès se font via un chemin profond particulier, cependant, il n'y aura rien pour ce chemin profond à coupler.
supercat
@Telastyn Il est parfaitement correct de traverser une infrastructure de données pour accéder aux données en profondeur. Ce n'est pas la même chose que les appels de méthode imbriqués. Vous devez parfois connaître une infrastructure de données et comment elle est imbriquée, il n'en va pas de même pour des objets comme un service et ses propres services / référentiels imbriqués, etc.
Per Hornshøj-Schierbeck