oop - responsabilidad - principios solid youtube
¿No es información experta/Tell Do not Ask en desacuerdo con el principio de responsabilidad única? (6)
Probablemente soy 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 de lo que estoy hablando:
Código que favorece a SRP pero viola Tell Tell not Ask, Info Expert:
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 Tell Do not Ask / Info Expert pero viola a 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 -
Experto en información: los objetos que tienen los datos necesarios para la operación deben alojar la operación
Tell No preguntar: no pidas datos a los objetos para hacer el trabajo; Dile a los objetos que hagan el trabajo
Principio de responsabilidad única: cada objeto debe tener una responsabilidad estrechamente definida
Esas 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 la "pureza", tendría otra clase cuyo trabajo consistiría en 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 cambiantes formen el problema, hágalo. La complejidad innecesaria es definitivamente algo que hay que evitar.
Recomiendo, por cierto, los Patrones Ágiles, Prácticas y principios de Robert C. Martin para tratamientos mucho más profundos de este tema.
No creo que estén tan en desacuerdo, ya que están enfatizando diferentes cosas que te causarán 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ás viendo hoy, debes decidir cuáles son los factores más importantes que probablemente te causen dolor.
Puede hablar sobre "No preguntar" cuando pida el estado del objeto para decirle al objeto que haga algo.
En su primer ejemplo TransferObjectFactory.createDe 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.
No estoy 100% de acuerdo con los dos ejemplos como representativos, pero desde una perspectiva general parece que estás 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 de control pase instancias de los otros objetos que está utilizando a los objetos especializados que ha extraído, usted 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 está diciendo a los objetos especializados que está componiendo juntos que hagan lo que sea que hagan, a El uno al otro).
Es una solución de composición que depende de un controlador de algún tipo para coordinar y delegar entre otros objetos sin enredarse en sus detalles internos.
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 DTO dentro del cliente? Si no tiene cuidado, CustomerDTO necesitará un Cliente, y un Cliente necesitará un ClienteDTO.
TellDontAsk dice que si se basa una decisión sobre 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 desea recordarle al Cliente que pague las facturas pendientes, de modo que llame
List<Bill> bills = Customer.GetOutstandingBills();
PaymentReminder.RemindCustomer(customer, bills);
esto es una violación En cambio, quieres hacer
Customer.RemindAboutOutstandingBills()
(y, por supuesto, deberá pasar el aviso de pago como una dependencia en la construcción del cliente).
Information Expert dice lo mismo más o menos.
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.
Craig Larman discutió esto cuando introdujo GRASP en la aplicación de UML y patrones para el análisis orientado a objetos y diseño y desarrollo iterativo (2004):
En algunas situaciones, una solución sugerida por Expert es indeseable, generalmente debido a problemas de acoplamiento y 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]
Apoyar 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 significa que CustomerDTO es el Experto y debe saber cómo crearse dado un Cliente:
CustomerDTO dto = new CustomerDTO(bob);
Si eres alérgico a lo nuevo, puedes ir estático:
CustomerDTO dto = CustomerDTO.buildFor(bob);
O bien, si odias a los dos, 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);
}
}