2009-07-20 15 views
5

El Law of Demeter no impide el paso de objetos a los constructores de clase. Sin embargo, prohíbe recuperar ese mismo objeto más adelante y llamar a un método para obtener un valor escalar. En cambio, se supone que se debe crear un método proxy que devuelve el valor escalar. Mi pregunta es, ¿por qué es aceptable pasar un objeto a un constructor de clase pero es inaceptable recuperar el mismo objeto más tarde y extraer un valor de él?Ley de Demeter y constructores de clase

+0

¿Puede proporcionar un enlace a la "Ley del diámetro"? – gahooa

Respuesta

13

Porque la Ley de Demeter dice que no debe diseñar la interfaz externa de un objeto para que parezca como si estuviera compuesto de otros objetos con interfaces conocidas, que los clientes puedan simplemente agarrar y acceder.

Pasas un objeto al constructor para decirle a tu nuevo objeto cómo comportarse, pero no es de tu incumbencia si el objeto mantiene ese objeto de parámetro, o guarda una copia de él, o simplemente lo mira una vez. olvida que alguna vez existió Al tener un método getMyParameterBack, ha comprometido todas las implementaciones futuras para poder producir ese objeto completo bajo demanda, y todos los clientes se acoplarán con dos interfaces en lugar de una.

Por ejemplo, si pasa un parámetro de URL al constructor de su objeto HTTPRequest, eso no significa que HTTPRequest debe tener un método getURL que devuelva un objeto URL en el que se espera que el llamante llame a getProtocol, getQueryString, etc. Si alguien que tiene un objeto HTTPRequest podría querer conocer el protocolo de la solicitud, debería (la Ley dice) averiguar llamando a getProtocol sobre el objeto que tienen, no sobre algún otro objeto que saben que HTTPRequest está almacenando internamente.

La idea es reducir el acoplamiento: sin la Ley de Demeter, el usuario debe conocer la interfaz HTTPRequest y URL para obtener el protocolo. Con la ley, solo necesitan la interfaz para HTTPRequest. Y HTTPRequest.getProtocol() claramente puede devolver "http" sin necesidad de que algún objeto URL participe en la discusión.

El hecho de que a veces el usuario del objeto de la solicitud sea el que lo creó y, por lo tanto, utiliza la interfaz URL para pasar el parámetro, no está ni aquí ni allí. No todos los usuarios de objetos HTTPRequest los habrán creado ellos mismos. Entonces, los clientes que tienen derecho según la Ley para acceder a la URL porque la crearon ellos mismos, pueden hacerlo de esa manera en lugar de recuperarla de la Solicitud. Los clientes que no crearon la URL no pueden.

Personalmente, creo que la Ley de Demeter, como se suele indicar en forma simple, está rajada. ¿Están diciendo seriamente que si mi objeto tiene un campo Nombre de cadena y quiero saber si el Nombre contiene caracteres que no sean ASCII, entonces debo definir un método NameContainsNonASCIICharacters en mi objeto en lugar de mirar la cadena en sí, o bien agregar una función visitName a la clase que toma una función de devolución de llamada para evitar la restricción al asegurar que la cadena es un parámetro de una función que he escrito? Eso no cambia el acoplamiento en absoluto, simplemente reemplaza los métodos getter con los métodos de visitante. ¿Debería cada clase que devuelve un número entero tener un conjunto completo de operaciones aritméticas, en caso de que quiera manipular el valor de retorno? getPriceMultipliedBy (int n)? Seguramente no.

Lo que es útil, es que cuando la rompes puedes preguntarte por qué la estás rompiendo y si podrías diseñar una mejor interfaz si no la rompes. Con frecuencia puede, pero realmente depende de qué tipo de objetos está hablando. Ciertas interfaces se pueden acoplar de forma segura contra vastas franjas de código, como enteros, cadenas e incluso URL, que representan conceptos ampliamente utilizados.

+0

Mira, un contenedor Uri es exactamente el tipo de clase ubicua que exentaría de esta regla, como una cadena. Visto como un medio para un fin, esta es una buena regla general, pero no es un fin en sí mismo. El objetivo es ocultar los detalles de la implementación interna, no jugar tímido. –

+0

Sí, no tendría ningún argumento en absoluto si se llamara la Guía de Demeter, pero no sirve de nada ser tímido al tratar de rebajar gatos aconsejar a los programadores. No tengo dudas de que, como ejercicio académico, aplicarlo como Ley es interesante y fructífero, y ofrece muchas ideas para mejorar el diseño de la interfaz. IMO dibuja la línea tan pronto como inserta rutinariamente una interfaz en otra. A veces, una X realmente solo tiene una Y, y todos lo saben :-) –

+0

Bien dicho. Mi preocupación sobre gatos insanos programadores típicos es que algunos tienen una tendencia a ver las buenas ideas como mágicas. El ejemplo estereotípico es el recién graduado que lee el libro de GoF sobre patrones de diseño y luego se va a cambiar TODO en un Singleton. –

4

La idea es que solo hable con sus amigos más cercanos. Así pues, usted no hace esto ...

var a = new A(); 
var x = a.B.doSomething(); 

En lugar de hacer esto ...

var a = new A(); 
var x = a.doSomething(); // where a.doSomething might call b.doSomething(); 

Tiene sus ventajas, ya que las cosas se vuelven más simples para las personas que llaman (Car.Start() frente Car.Engine.Start()), pero obtienes muchos pequeños métodos de envoltura. También puede usar el Mediator pattern para mitigar este tipo de "violación".

+0

Nunca había oído hablar de la Ley de Demeter, así que ... tiene perfecto sentido que se requieran funciones de conveniencia que deleguen a B, pero ¿está prohibido "B getB() const"? Si es así, ¿por qué? –

+0

Esa es toda la idea de la ley ... podría repetirse "solo hablas con tus amigos más cercanos". ¿Cuál es el beneficio? Esta idea ha existido por mucho tiempo. En estos días, estamos escribiendo aplicaciones muy ligeramente acopladas por supuesto. –

4

La respuesta de JP es bastante buena, así que esto es solo un complemento, no un desacuerdo u otro reemplazo.

La forma en que entiendo esta heurística es que una llamada a A no se debe interrumpir debido al cambio de clase B. Entonces, si encadenas tus llamadas con a.b.foo(), entonces la interfaz de A se vuelve dependiente de B, violando la regla. En cambio, se supone que debes llamar a .BFoo(), que llama a b.foo() por ti.

Esta es una buena regla empírica, pero puede llevar a código incómodo que realmente no aborda la dependencia tanto como lo consagra. Ahora A tiene que ofrecer BFoo para siempre, incluso cuando B ya no ofrece Foo. No hay mucho de una mejora y podría decirse que es mejor en al menos algunos casos si los cambios en B rompieron la llamada que quiere Foo, no B en sí.

También agregaría que, estrictamente hablando, esta regla se rompe constantemente para un cierto grupo de clase ubicua, como la cadena. Quizás sea aceptable decidir qué clases son igualmente ubicuas dentro de una capa particular de una aplicación e ignorar libremente la "Regla" de Demeter para ellas.

+0

Tengo que hacerte un +1 desde que dijiste lo mismo que yo solo más corto y más rápido :-) –

+0

Amd Te devolví el favor ya que incluiste un buen ejemplo. –