2008-10-04 10 views
24

Probablemente sea solo yo, por eso estoy haciendo la pregunta. Information Expert, Tell Do not Ask y SRP a menudo se mencionan juntos como mejores prácticas. Pero creo que están en desacuerdo. Esto es lo que estoy hablando:¿No es información experta/informativa que no se contradiga con el principio de responsabilidad única?

Código

que favorece SRP pero viola Dile No preguntar, Información Experto:

Customer bob = ...; 
// TransferObjectFactory has to use Customer's accessors to do its work, 
// violates Tell Don't Ask 
CustomerDTO dto = TransferObjectFactory.createFrom(bob); 

Código que favorece Dile No preguntar/Información de expertos, pero viola SRP:

Customer bob = ...; 
// Now Customer is doing more than just representing the domain concept of Customer, 
// violates SRP 
CustomerDTO dto = bob.toDTO(); 

Si están en desacuerdo, eso es una reivindicación de mi TOC. De lo contrario, infórmenme sobre cómo estas prácticas pueden coexistir pacíficamente. Gracias.

Editar: alguien quiere una definición de los términos -

información Expert: Los objetos que tienen los datos necesarios para la operación debe acoger la operación

Dile No preguntar: no pregunte objetos para datos para hacer el trabajo; decir a los objetos para hacer el trabajo

Responsabilidad solo principio: cada objeto debe tener una responsabilidad en sentido estricto

+0

Una pequeña presentación de los términos que está utilizando podría haber sido útil. – shoosh

+3

La respuesta corta es sí ... la larga es que a veces las personas (académicos) se quedan con la cabeza tan metida en el culo que olvidan que la programación aún no es una ciencia perfecta, ... no tenemos un lenguaje que sea lo suficientemente limpio como para satisfacer todos estos principios. Rompe el principio de que tiene más sentido. – Stimul8d

+1

Su segundo ejemplo infringe SRP, pero realmente no es un buen ejemplo de Tell Do not Ask. – koenmetsu

Respuesta

8

no creo que sean tan en desacuerdo, ya que están haciendo hincapié en diferentes cosas que le causan dolor . Una es sobre la estructuración del código para dejar en claro dónde están las responsabilidades particulares y la reducción del acoplamiento, y la otra sobre la reducción de las razones para modificar una clase.

Todos tenemos que tomar decisiones todos los días sobre cómo estructurar el código y qué dependencias estamos dispuestos a introducir en los diseños.

Hemos desarrollado una gran cantidad de pautas útiles, máximas y patrones que pueden ayudarnos a tomar decisiones.

Cada uno de estos es útil para detectar diferentes tipos de problemas que podrían estar presentes en nuestros diseños. Para cualquier problema específico que pueda estar mirando, habrá un lugar agradable en alguna parte.

Las diferentes pautas se contradicen entre sí. Solo aplicar cada pieza de orientación que haya escuchado o leído no mejorará su diseño.

Para el problema específico que está viendo hoy, debe decidir cuáles son los factores más importantes que pueden causarle dolor.

+0

entonces, _todos_ principios SÓLIDOS, al menos en algunos casos, no se pueden cumplir de manera simultánea, es decir, pueden dañar y no son "la mayoría de todas ellas las mejores" :) – mlvljr

+6

Sí, de hecho. Es por eso que la programación es un arte y no la aplicación ciega de las reglas. – DJClayworth

2

Estas clases no están en desacuerdo. El DTO simplemente sirve como un conducto de datos de almacenamiento que está destinado a ser utilizado como un contenedor tonto. Ciertamente no viola el SRP.

Por otro lado, el método .toDTO es cuestionable: ¿por qué debería el Cliente tener esta responsabilidad? Por el bien de "pureza", tendría otra clase cuyo trabajo era crear DTO a partir de objetos comerciales como Cliente.

No olvide que estos principios son principios, y cuando puede obtener soluciones más simples hasta que los requisitos cambien forcen el problema, hágalo. La complejidad innecesaria es definitivamente algo que hay que evitar.

Recomiendo mucho, por cierto, los Patrones ágiles de Robert C. Martin, Prácticas y principios para tratamientos mucho más profundos de este tema.

5

Puede hablar sobre "No preguntar" cuando pida el estado del objeto para decirle al objeto que haga algo.

En su primer ejemplo TransferirFábrica de objetos.createDesde solo un convertidor. No le dice al objeto del Cliente que haga algo después de inspeccionar su estado.

Creo que el primer ejemplo es correcto.

0

No estoy 100% de acuerdo con sus dos ejemplos como representativos, pero desde una perspectiva general, parece estar razonando desde la suposición de dos objetos y solo dos objetos.

Si separa el problema aún más y crea uno (o más) objetos especializados para asumir las responsabilidades individuales que tiene, y luego hace que el objeto controlador pase instancias de los otros objetos que está utilizando a los objetos especializados que tiene tallado, debería ser capaz de observar un compromiso feliz entre SRP (cada responsabilidad ha sido manejada por un objeto especializado), y Tell Do not Ask (el objeto de control le dice a los objetos especializados que está componiendo juntos que hagan lo que sea que sea lo hacen, el uno al otro).

Es una solución de composición que se basa en un controlador de algún tipo para coordinar y delegar entre otros objetos sin quedar atascado en sus detalles internos.

1

Los DTO con una clase hermana (como usted) violan los tres principios que usted indicó y la encapsulación, que es la razón por la que está teniendo problemas aquí.

¿Para qué está utilizando este CustomerDTO y por qué no puede simplemente usar Customer y tener los datos de los DTO dentro del cliente? Si no tiene cuidado, CustomerDTO necesitará un Cliente, y un Cliente necesitará un ClienteDTO.

TellDontAsk dice que si está basando una decisión en el estado de un objeto (por ejemplo, un cliente), entonces esa decisión debe realizarse dentro de la clase del cliente.

Un ejemplo es si usted quiere recordar al cliente para pagar todas las facturas pendientes, por lo que llaman

List<Bill> bills = Customer.GetOutstandingBills(); 
    PaymentReminder.RemindCustomer(customer, bills); 

se trata de una violación. Por el contrario desea hacer

Customer.RemindAboutOutstandingBills() 

(y por supuesto que tendrá que pasar en el PaymentReminder como una dependencia de la construcción del cliente).

Información experto dice lo mismo.

El Principio de Responsabilidad Individual puede malinterpretarse fácilmente: dice que la clase de cliente debe tener una responsabilidad, pero también que la responsabilidad de agrupar datos, métodos y otras clases alineadas con el concepto de Cliente debe ser encapsulada por una sola clase. Lo que constituye una responsabilidad única es extremadamente difícil de definir exactamente y recomendaría más lectura sobre el tema.

1

Craig Larman discutió este cuando introdujo GRASP en la aplicación de UML y Patrones a Orientada a Objetos Análisis y Diseño y desarrollo iterativo (2004):

En algunas situaciones, una solución sugerida por los expertos es indeseable, por lo general debido a problemas en el acoplamiento y la cohesión (estos principios se discuten más adelante en este capítulo).

Por ejemplo, ¿quién debería ser responsable de guardar una Venta en una base de datos? Ciertamente, gran parte de la información que se guardará está en el objeto Venta, y por lo tanto el Experto podría argumentar que la responsabilidad recae en la clase Venta. Y, por extensión lógica de esta decisión, cada clase tendría sus propios servicios para salvarse en una base de datos. Pero actuar según ese razonamiento conduce a problemas en la cohesión, el acoplamiento y la duplicación. Por ejemplo, la clase Venta ahora debe contener lógica relacionada con el manejo de la base de datos, como la relacionada con SQL y JDBC (Conectividad de base de datos Java). La clase ya no se enfoca solo en la lógica de la aplicación pura de "ser una venta". Ahora otros tipos de responsabilidades reducen su cohesión. La clase debe estar acoplada a los servicios de bases de datos técnicas de otro subsistema, como los servicios JDBC, en lugar de estar simplemente acoplados a otros objetos en la capa de dominio de los objetos de software, por lo que su acoplamiento aumenta. Y es probable que una lógica de base de datos similar se duplique en muchas clases persistentes.

Todos estos problemas indican la violación de un principio arquitectónico básico: diseño para una separación de las principales preocupaciones del sistema. Mantenga la lógica de la aplicación en un lugar (como los objetos de software de dominio), mantenga la lógica de la base de datos en otro lugar (como un subsistema de servicios de persistencia separado), etc., en lugar de mezclar preocupaciones de sistema diferentes en el mismo componente. [11]

Respaldar una separación de las principales preocupaciones mejora el acoplamiento y la cohesión en un diseño. Por lo tanto, aunque por Expertos podríamos encontrar alguna justificación para asignar la responsabilidad de los servicios de bases de datos en la clase de Venta, por otras razones (generalmente cohesión y acoplamiento), terminaríamos con un diseño deficiente.

Por lo tanto, el SRP generalmente triunfa sobre Information Expert.

Sin embargo, el Principio de Inversión de Dependencia puede combinarse bien con el Experto. El argumento aquí sería que el Cliente no debería tener una dependencia de CustomerDTO (general a los detalles), sino al revés. Esto significaría que CustomerDTO es el experto y debe saber cómo construir en sí dado un cliente:

CustomerDTO dto = new CustomerDTO(bob); 

Si usted es alérgico a nuevo, usted podría ir estática:

CustomerDTO dto = CustomerDTO.buildFor(bob); 

O, si odio a ambos, volvemos a AbstractFactory:

public abstract class DTOFactory<D, E> { 
    public abstract D createDTO(E entity); 
} 


public class CustomerDTOFactory extends DTOFactory<CustomerDTO, Customer> { 
    @Override 
    public CustomerDTO createDTO(Customer entity) { 
     return new CustomerDTO(entity); 
    } 
} 
Cuestiones relacionadas