¿El nuevo operador condicional nulo de C # 6.0 va en contra de la Ley de Demeter?

29

La Ley de Deméter establece lo siguiente:

  • Cada unidad debe tener un conocimiento limitado sobre otras unidades: solo unidades "estrechamente" relacionadas con la unidad actual.
  • Cada unidad solo debe hablar con sus amigos; No hables con extraños.
  • Solo habla con tus amigos inmediatos.

C # 6.0 introdujo un nuevo operador llamado operador condicional nulo . En mi humilde opinión, facilita la codificación y mejora la legibilidad. Pero también hace que sea más fácil escribir más código acoplado, ya que es más fácil navegar por los campos de clase, ya verificando la nulidad (algo así como var x = A?.B?.C?.D?.E?.F?).

¿Es correcto afirmar que este nuevo operador va en contra de la Ley de Demeter?

Arthur Rizzo
fuente
2
¿Por qué crees que eso A?.B?.C?.D?.E?.F?lo violaría? LoD no se trata de cuántos puntos y si el método de llamada tiene tal información sobre la estructura que no está en violación de sus puntos, tal llamada sería perfectamente aceptable. Que dicho código podría violar LdD no es suficiente decir que todos los usos de que hacer violan LdD.
14
Lectura " La ley de Demeter no es un ejercicio de conteo de puntos "? Discute este ejemplo exacto.
outis
@outis: excelente lectura. No estoy diciendo que cada código en forma de X.Y.Z.W.Uviolación de la "ley". Pero, en mi experiencia con el código, el 90% de las veces es un código simple y feo.
Arthur Rizzo
2
@ArthurRizzo, pero eso no es un problema con el operador condicional nulo que va en contra de LoD. Ese es el código que tiene la culpa. El operador es solo una herramienta para simplificar la lectura humana. El .?no viola más LoD que +o lo -hace.
1
RC Martin distingue entre clases de datos puros y clases de comportamiento. Si las Propiedades a las que se accede exponen datos internos de una clase de comportamiento, el fragmento ciertamente viola la LoD, pero esto no tiene nada que ver con el operador condicional nulo. De todos modos, las propiedades no están obligadas a exponer datos internos, lo que podría ser un olor, pero no viola la LoD. Según RC Martin, el esquema puede ser absolutamente válido con clases de datos puros.
Paul Kertscher

Respuestas:

44

¿Es correcto afirmar que este nuevo operador va en contra de la Ley de Demeter?

No *


* El operador condicional nulo es una herramienta dentro del lenguaje y el marco .NET. Cualquier herramienta tiene la capacidad de ser abusada y utilizada de manera que pueda dañar la capacidad de mantenimiento de una aplicación determinada.

Pero el hecho de que una herramienta puede ser objeto de abuso no necesariamente significa que tiene que ser objeto de abuso, ni que la herramienta viola cualquier principio en particular (s) que pueden poseer.

La Ley de Demeter y otras son pautas sobre cómo debe escribir su código. Está dirigido a humanos, no a las herramientas. Por lo tanto, el hecho de que el lenguaje C # 6.0 tenga una nueva herramienta no necesariamente afecta la forma en que debería escribir y estructurar su código.

Con cualquier nueva herramienta, es necesario evaluarlo como ... si el tipo que termina el mantenimiento de su código será un psicópata violento ... . Tenga en cuenta nuevamente, que esto es una guía para la persona que escribe el código y no sobre las herramientas que se utilizan.

Comunidad
fuente
foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1);estaría bien (y no peor que foo[0][0][0][0][0] = 1) ... y muchas otras situaciones en las que eso no viola LoD.
@MichaelT Cuando comienzas a entrar en matrices de esa dimensionalidad, parece que sería más fácil tratar los índices como un vector / tupla / matriz en sí y dejar que las partes internas de la clase de matriz se preocupen por cómo se almacenan realmente los datos. (Lo que, ahora que lo pienso, es una aplicación de la Ley de Deméter, al menos en lo que se refiere a la encapsulación.)
JAB
(Y, por supuesto, ese tipo de práctica hace que sea más fácil implementar el corte multidimensional y tener algunas herramientas de matriz realmente poderosas.)
JAB
1
@JAB Solo estaba tratando de encontrar un ejemplo. Probablemente sea mejor Dom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ...: es una cuestión de procesar la estructura de algún código tonto. No es un extraño ... es simplemente tonto. El se .?vuelve muy útil en tales estructuras.
10

Más o menos.

Si solo está haciendo un acceso ( a?.Foo), entonces es equivalente a:

a == null ? null : a.Foo

que la mayoría de la gente estaría de acuerdo no es una violación de la Ley de Demeter. En ese punto, es solo azúcar sintáctico para mejorar la legibilidad.

Algo más que eso, y probablemente violaría la Ley de Demeter, y esta característica tiende a promover ese tipo de uso. Incluso diría que el uso "bueno" anterior solo no es suficiente para garantizar este tipo de cambio en el idioma, por lo que espero que se haya hecho para admitir el uso claramente menos bueno.

Dicho esto, vale la pena recordar que la Ley de Demeter no es una ley per se, sino más bien una guía. Gran cantidad de código lo viola y funciona bien. A veces, la simplicidad del diseño o del código vale más que el riesgo que supone violar la Ley de Demeter.

Telastyn
fuente
cualquier cosa más que eso no necesariamente rompe LoD, por ejemplo, el patrón de construcción
jk
@Telastyn: La nueva sintaxis del lenguaje que estamos hablando de hace Método de llamadas de soporte: a?.Func1(x)?.Func2(y) El operador nula coalescencia es otra cosa.
Ben Voigt
@BenVoigt: ah, estaba saliendo del artículo, que indicaba que solo funcionaba con campos, propiedades e indexadores. No tenía MSVS2015 a mano para probar. Tienes razón.
Telastyn
1
a? .Foo no es del todo equivalente a a == nulo? nulo: a.Foo. El primero evalúa solo una vez, el segundo lo evalúa dos veces. Eso podría importar si a fuera un iterador.
Loren Pechtel
9

No. Consideremos tanto al operador por sí solo como al uso fuertemente encadenado que tiene para él.

Por sí solo .?Adepende de la misma cantidad de conocimiento de la clase que es el valor izquierdo y del tipo devuelto por el método como lo .A != nullhace, a saber. Necesita saber que la Apropiedad existe y devuelve un valor con el que se puede comparar null.

Solo podemos argumentar que esto viola la ley de Demeter si las propiedades escritas lo hacen. Ni siquiera estamos obligados a tener Aun tipo concreto (su valor podría ser de tipo derivado). El acoplamiento aquí es mínimo.

Ahora consideremos var x = A?.B?.C?.D?.E?.F.

Lo que significa que Adebe ser de un tipo que podría ser nulo, o podría tener una Bpropiedad, que debe ser de un tipo que podría ser nulo o tener una Cpropiedad, y así sucesivamente hasta que el tipo de Epropiedad sea algo que podría ser nulo o Podría tener una Fpropiedad.

En otras palabras, debemos hacerlo con un lenguaje de tipo estático o aplicando una restricción a los tipos que se pueden devolver si el tipo de letra es suelto. C # en la mayoría de los casos usa escritura estática, por lo que no hemos cambiado nada.

Si lo tuviéramos, el siguiente código también violaría la ley:

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

Que es exactamente lo mismo . Este código que usa el acoplamiento de diferentes elementos necesita "saber" sobre la cadena completa de acoplamiento, pero está usando un código que no viola la Ley de Demeter para hacerlo, con cada unidad que tiene un acoplamiento bien definido con el siguiente.

Jon Hanna
fuente
3
+1 el nuevo operador es simplemente azúcar sintáctico para la receta amarga que has descrito.
Ross Patterson
1
Bueno, si un desarrollador escribe un código que se parece a eso, creo que es más fácil notar que algo puede no estar bien. Sé que el operador es 100% azúcar sintético, pero aún así, creo que las personas tienden a sentirse más cómodas escribiendo algo así como var x = A?.B?.C?.D?.E?.Ftodos esos if / elses, incluso si al final son iguales.
Arthur Rizzo
2
Es más fácil notar que algo no está bien A?.B?.C?.D?.E?.Fporque hay menos cosas que pueden estar mal; o deberíamos estar tratando de llegar a Ftravés de ese camino, o no deberíamos, mientras que la forma más larga podría tener errores, así como el error de que no es lo correcto.
Jon Hanna
@ArthurRizzo Pero si asocia el tipo de código anterior con violaciones de LoD, entonces es fácil pasarlas por alto en el caso de que no se necesite una verificación nula y simplemente puede hacerlo A.B.C.D. Es mucho más simple tener una sola cosa a tener en cuenta (acceso a la propiedad encadenada) en lugar de dos cosas diferentes que dependen de un detalle bastante irrelevante (verificación nula)
Ben Aaronson
5

Los objetos pueden crearse con el fin de encapsular comportamientos o mantener datos, y los objetos pueden crearse con el fin de compartirlos con código externo o mantenerlos en privado con su creador.

Los objetos que se crean con el fin de encapsular el comportamiento (ya sea compartido o no), o para ser compartido con el código externo (ya sea que encapsulan el comportamiento o los datos) generalmente se debe acceder a través de su interfaz de superficie. Sin embargo, cuando los objetos que contienen datos se crean para uso exclusivo de su creador, las razones normales de la Ley del Demeter para evitar el acceso "profundo" no se aplican. Si parte de una clase que almacena o manipula datos en el objeto se cambia de una manera que requeriría ajustar otro código, será posible garantizar que todo ese código se actualice porque, como se indicó anteriormente, el objeto fue creado para El uso exclusivo de una clase.

Mientras pienso en el?. El operador quizás podría haber sido mejor diseñado, hay suficientes situaciones en las que los objetos hacen uso de estructuras de datos anidados que el operador tiene muchos casos de uso que no violarían los principios expresados ​​por la Ley de Demeter. El hecho de que pueda usarse para violar el LoD no debe tomarse como un argumento en contra del operador, ya que no es peor que el "." operador en ese sentido.

Super gato
fuente
¿Por qué la Ley de Demeter no se aplica a los objetos que contienen datos?
Telastyn
2
@Telastyn: El objetivo de la LoD es evitar los problemas que pueden surgir si una pieza de código accede a objetos internos que otra cosa podría manipular o importar . Si nada más en el universo podría manipular o preocuparse por el estado de los objetos internos, entonces no hay necesidad de protegerse contra tales problemas.
supercat
No estoy seguro de estar de acuerdo. No es que otras cosas puedan importar modificar los datos, es que se está acoplando al objeto contenido a través de una ruta (esencialmente tres puntos de acoplamiento: los dos objetos y su relación). A veces ese acoplamiento no va a ser un gran problema, pero todavía me parece maloliente.
Telastyn
@Telastyn: Su punto sobre los caminos es bueno, pero creo que mi punto se mantiene bien. Acceder a un objeto a través de múltiples rutas crea acoplamiento entre esas rutas. Si algún acceso es a través de una ruta poco profunda, entonces el acceso a través de una ruta profunda también puede causar un acoplamiento no deseado. Si todo el acceso es a través de una ruta profunda particular, sin embargo, no habrá nada para que esa ruta profunda se acople.
supercat
@Telastyn Está perfectamente bien atravesar una estructura de datos para llegar a los datos en el fondo. No es lo mismo que las llamadas a métodos anidados. A veces se requiere que conozca una estructura de datos y cómo está anidada, lo mismo no se aplica a objetos como un servicio y sus propios servicios / repositorios anidados, etc.
Según Hornshøj-Schierbeck