En una edición reciente del podcast DotNet & More, discutimos con Maxim Arshinov su próximo informe sobre Moscú. Siguiente "El modelo de brillo y pobreza del sujeto". Se podrá acceder fácilmente a la posición de Maxim directamente en la conferencia. Y, además, me gustaría considerar la visión del gran debate de los modelos de dominio Anemic VS "Rich" ("Rich") a través del prisma de las plantillas GRASP, una vez populares
Las discusiones han estado sucediendo durante mucho tiempo, por ejemplo:
Antes de comenzar el análisis, me gustaría aclarar el tema de la disputa. La principal diferencia entre el modelo anémico y el rico es que no contiene lógica empresarial en el cuerpo de la clase. Un ejemplo bien conocido del modelo anémico puede ser el patrón DTO bien conocido.
Un modelo "rico", a su vez, puede contener lógica que describe las reglas de negocios, funciones , etc. Tenga en cuenta que estamos considerando los métodos que reflejan la lógica empresarial. ToString, GetHashCode y otras partes "técnicas" de las clases no se incluyen en el tema de discusión y, por lo tanto, se ignoran.
GRASP
Como se señaló al comienzo del artículo, consideraremos este problema en el contexto de los patrones GRASP . Este conjunto de patrones fue presentado en el libro de Craig Larman "El uso de UML y patrones de diseño" e influyó mucho en la programación moderna, por ejemplo, las reglas de bajo acoplamiento / alta cohesión se anunciaron en GRASP.
Para las personas familiarizadas con los patrones GoF, este conjunto de patrones puede parecer demasiado borroso. La cuestión es que los patrones GRASP se centran no en resolver problemas aplicados, sino en la distribución de la responsabilidad de ciertas acciones y operaciones entre objetos:
- El creador es responsable de crear objetos.
- El controlador es responsable de las operaciones de los usuarios.
- La indirecta es responsable de organizar mallas débiles entre objetos.
Y así sucesivamente.
En el contexto de este artículo, me gustaría centrarme en los siguientes patrones:
- Experto en información
- Fabricación pura
Problema: ¿Cuál es el principio básico de compartir responsabilidades entre las instalaciones?
Solución: asigne este deber a una clase que tenga información suficiente para llevarlo a cabo.
En otras palabras:
La responsabilidad debe asignarse a quien posee la máxima información necesaria para la ejecución: el experto en información.
En el contexto de C # : se debe declarar un método en la clase cuyos campos y propiedades se utilizan en este método.
Por ejemplo, si para calcular el monto de la deuda toda la información necesaria está en la esencia del deudor (monto, tiempo del préstamo), entonces es en esta esencia que se debe anunciar el método apropiado.
Por supuesto, esta simplificación es algo excesiva, pero el punto principal debe ser claro.
Fabricación pura
Problema: qué clase debería proporcionar la implementación de alta cohesión y bajo acoplamiento si la plantilla de experto en información no proporciona una solución adecuada.
Solución: asigne un grupo de tareas con un alto grado de compromiso a una clase artificial que no represente un concepto específico del área temática.
En otras palabras:
Si no es posible seleccionar inequívocamente la entidad apropiada del modelo de asignatura a la que se le podría asignar responsabilidad, es necesario crear una clase sintética que no exista en el área de asignatura.
En el contexto de C # : si la implementación del método proporciona el uso uniforme de varias entidades o dependencias externas, entonces este método debe declararse en una clase separada. Y estas clases se llaman servicios.
Por ejemplo, si para calcular el monto de la deuda necesita conocer no solo los atributos del deudor, sino también los parámetros del banco (tasa de interés, período de retraso permitido), entonces vale la pena crear un servicio separado que contenga el método apropiado.
¿Y qué significa todo esto?
En el contexto de lo anterior, podemos distinguir un algoritmo bastante simple que ayuda a tomar una decisión, colocar el método en un modelo o en un servicio :
- Si un método usa solo propiedades del modelo, debe declararse en el modelo
- Si un método usa, en su mayor parte, las propiedades de un modelo y solo parcialmente otro, puede declararse en el modelo
- Si el método usa uniformemente las propiedades de varios modelos, entonces vale la pena trasladarlo a un servicio separado o existente
- Si un método usa una dependencia externa, por ejemplo, un Repositorio, se debe mover a un servicio separado o existente
Resumen
Los patrones GRASP no pueden ser vistos como la verdad última. Pero pueden ayudar a comprender fácilmente a qué objeto asignar este o aquel comportamiento. En consecuencia, la disputa es anémica o el modelo "rico" puede no tener sentido, ya que la decisión se toma en cada caso, dependiendo del modelo del área temática y el comportamiento asociado con ella. En el proceso de diseño del sistema, aparecerán ambas entidades con y sin comportamiento. Y esto es bastante correcto.