resueltos online modelo introduccion herencia ejercicios ejemplos dominio domain diagrama analisis java architecture domain-driven-design anti-patterns anemic-domain-model

java - online - Ejemplos concretos de por qué el ''Modelo de dominio anémico'' se considera un antipatrón



modelo de dominio introduccion (6)

Pido disculpas si este es un duplicado, pero no pude encontrar ningún ejemplo concreto sobre el tema en preguntas relacionadas.

Después de leer el artículo de Martin Fowler sobre el ''Modelo de dominio anémico'' , me quedo preguntándome por qué se considera un antipatrón. ¿Incluso la mayoría de los desarrolladores empresariales lo consideran un anti-patrón, ya que AFAIK probablemente el 90% de las aplicaciones j2ee están diseñadas de forma "anémica"?

Alguien puede recomendar una lectura adicional sobre el tema (que no sea el libro ''Diseño impulsado por dominio''), o incluso mejor, dar ejemplos concretos de cómo este antipatrón está afectando el diseño de la aplicación de una mala manera.

Gracias,


Bien. Tienes razón en que casi todo el código Java está escrito de esta manera. La razón por la que es un antipatrón es que uno de los principios fundamentales del diseño orientado a objetos es combinar los datos y las funciones que operan en un solo objeto. Por ejemplo, cuando estaba escribiendo el código c de la vieja escuela, imitamos el diseño orientado a objetos como este:

struct SomeStruct { int x; float y; }; void some_op_i(SomeStruct* s, int x) { // do something } void some_op_f(SomeStruct* s, float y) { // something else }

Es decir, el lenguaje no nos permitió combinar las funciones para operar en SomeStruct dentro de la estructura, por lo que creamos un grupo de funciones gratuitas que, por convención, tomaron SomeStruct como primer parámetro.

Cuando apareció c ++, la estructura se convirtió en una clase, y le permite poner funciones en la estructura (clase). Luego, la estructura se pasa implícitamente como el puntero de este, por lo que en lugar de crear una estructura y pasarla a las funciones, creas la clase y los métodos de llamada en su contra. El código es más claro y más fácil de entender de esta manera.

Luego me mudé al mundo java, y todos separan el modelo del servicio, es decir, el modelo es una estructura glorificada, y el servicio, al no tener estado como tal, se convierte en una colección de funciones que opera en un modelo. Lo que para mí, suena sospechosamente como un lenguaje de lenguaje ac. Es bastante divertido porque en c se hizo porque el lenguaje no ofrecía nada mejor, y en java se hizo porque los programadores no saben nada mejor.


Como con la mayoría de las cosas en el mundo del desarrollo de software, no hay blanco y negro. Hay casos en los que un modelo de dominio anémico es el ajuste perfecto.

PERO hay muchos casos en los que los desarrolladores intentan construir un modelo de dominio, también conocido como DDD, y terminan con un modo de dominio anémico. Creo que en este caso el modelo de dominio anémico se considera un antipater.

Solo asegúrese de usar la mejor herramienta para el trabajo y, si funciona, no se moleste en cambiarlo.


Dadas las siguientes dos clases:

class CalculatorBean { //getters and setters } class CalculatorBeanService { Number calculate(Number first, Number second); { //do calculation } }

Si lo comprendo correctamente, Fowler está diciendo que debido a que su CalculatorBean es solo un grupo de captadores / configuradores, no obtiene ningún valor real de él y si transfiere ese objeto a otro sistema, no hará nada. El problema parece que su CalculatorBeanService contiene todo de lo que CalculatorBean debería ser responsable. Lo que no es el mejor ya que ahora CalculatorBean delega toda su responsabilidad al CalculatorBeanService


Martin Fowler trae a esta industria muchas palabras y menos comprensión.

La mayoría de las aplicaciones de hoy (web / db) necesitan muchos objetos que exponen sus propiedades.

Cualquier autoridad (auto reivindicado) que frunció el ceño ante tal práctica debería dar ejemplo, y mostrarnos una aplicación exitosa en el mundo real que esté llena de encarnaciones de sus maravillosos principios.

O bien, cállate. Es repugnante que haya tantos aires calientes en nuestra industria. Esto es ingeniería, no un club de drama.


Para obtener la respuesta completa, eche un vistazo a mi blog que también contiene ejemplos de código fuente [blog]: https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/

Si observa el modelo de dominio anémico desde una perspectiva orientada a objetos, definitivamente es un antipatrón porque es una programación puramente procesal. La razón por la que se denomina antipatrón es que el principio principal orientado a objetos no está cubierto por un modelo de dominio anémico:

Orientado a objetos significa que: un objeto gestiona su estado y garantiza que está en un estado legal en cualquier momento. (ocultación de datos, encapsulación)

Por lo tanto, un objeto encapsula datos y administra el acceso y la interpretación de los mismos. En contraste con esto, un modelo anémico no garantiza que esté en un estado legal en cualquier momento.

Un ejemplo de un pedido con artículos de pedido ayudará a mostrar la diferencia. Así que echemos un vistazo a un modelo anémico de un pedido.

Un modelo anemico

public class Order { private BigDecimal total = BigDecimal.ZERO; private List<OrderItem> items = new ArrayList<OrderItem>(); public BigDecimal getTotal() { return total; } public void setTotal(BigDecimal total) { this.total = total; } public List<OrderItem> getItems() { return items; } public void setItems(List<OrderItem> items) { this.items = items; } } public class OrderItem { private BigDecimal price = BigDecimal.ZERO; private int quantity; private String name; public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } }

Entonces, ¿dónde está ubicada la lógica que interpreta el pedido y los artículos del pedido para calcular el total de un pedido? Esta lógica se coloca a menudo en clases llamadas * Helper, * Util, * Manager o simplemente * Service. Un servicio de pedidos en un modelo anémico se vería así:

public class OrderService { public void calculateTotal(Order order) { if (order == null) { throw new IllegalArgumentException("order must not be null"); } BigDecimal total = BigDecimal.ZERO; List<OrderItem> items = order.getItems(); for (OrderItem orderItem : items) { int quantity = orderItem.getQuantity(); BigDecimal price = orderItem.getPrice(); BigDecimal itemTotal = price.multiply(new BigDecimal(quantity)); total = total.add(itemTotal); } order.setTotal(total); } }

En un modelo anémico, invoca un método y le pasa el modelo anémico para llevar el modelo anémico a un estado legal. Por lo tanto, la gestión del estado del modelo anémico se coloca fuera del modelo anémico y este hecho lo convierte en un antipatrón desde una perspectiva orientada a objetos.

A veces verá una implementación de servicio ligeramente diferente que no modifica el modelo anémico. En su lugar, devuelve el valor que calcula. P.ej

public BigDecimal calculateTotal(Order order);

En este caso la Order no tiene un total propiedad. Si ahora hace que el Order inmutable, está en camino a la programación funcional. Pero este es otro tema que no puedo descubrir aquí.

Los problemas con el modelo de orden anémico anterior son:

  • Si alguien agrega un artículo de pedido al pedido, el valor de Order.getTotal() es incorrecto siempre y cuando no haya sido recalculado por el servicio de pedidos. En una aplicación del mundo real, puede ser complicado averiguar quién agregó el artículo del pedido y por qué no se ha llamado al OrderService. Como es posible que ya haya reconocido, la Orden también rompe la encapsulación de la lista de artículos de la orden. Alguien puede llamar a order.getItems().add(orderItem) para agregar un artículo de pedido. Eso puede dificultar la búsqueda del código que realmente agrega el elemento (la referencia de order.getItems() se puede pasar a través de toda la aplicación).
  • El método de cálculo total de OrderService es responsable de calcular el total de todos los objetos Order. Por lo tanto, debe ser apátrida. Pero sin estado también significa que no puede almacenar en memoria caché el valor total y solo recalcularlo si el objeto de Orden cambió. Entonces, si el método calculatotal toma mucho tiempo, también tiene un problema de rendimiento. Sin embargo, tendrá problemas de rendimiento, ya que los clientes podrían no saber si la Orden se encuentra en un estado legal o no y, por lo tanto, llamar preventivamente a calculateTotal(..) incluso cuando no es necesario.

También verá que a veces los servicios no actualizan el modelo anémico y, en cambio, simplemente devuelven el resultado. P.ej

public class OrderService { public BigDecimal calculateTotal(Order order) { if (order == null) { throw new IllegalArgumentException("order must not be null"); } BigDecimal total = BigDecimal.ZERO; List<OrderItem> items = order.getItems(); for (OrderItem orderItem : items) { int quantity = orderItem.getQuantity(); BigDecimal price = orderItem.getPrice(); BigDecimal itemTotal = price.multiply(new BigDecimal(quantity)); total = total.add(itemTotal); } return total; } }

En estos casos, los servicios interpretan el estado del modelo anémico en algún momento y no actualizan el modelo anémico con el resultado. El único beneficio de este enfoque es que el modelo anémico no puede contener un estado total no válido, porque no tendrá una propiedad total . Pero esto también significa que el total debe calcularse cada vez que se necesita. Al eliminar la propiedad total los desarrolladores pueden usar el servicio y no confiar en el estado de propiedad del total . Pero esto no garantizará que los desarrolladores almacenen en caché el valor total de alguna manera y, por lo tanto, también podrían usar valores que están desactualizados. Esta forma de implementar un servicio se puede hacer cada vez que una propiedad se deriva de otra propiedad. O en otras palabras ... cuando interpretas datos básicos. Por ejemplo, int getAge(Date birthday) .

Ahora eche un vistazo al modelo de dominio enriquecido para ver la diferencia.

El enfoque de dominio rico

public class Order { private BigDecimal total; private List<OrderItem> items = new ArrayList<OrderItem>(); /** * The total is defined as the sum of all {@link OrderItem#getTotal()}. * * @return the total of this {@link Order}. */ public BigDecimal getTotal() { if (total == null) { /* * we have to calculate the total and remember the result */ BigDecimal orderItemTotal = BigDecimal.ZERO; List<OrderItem> items = getItems(); for (OrderItem orderItem : items) { BigDecimal itemTotal = orderItem.getTotal(); /* * add the total of an OrderItem to our total. */ orderItemTotal = orderItemTotal.add(itemTotal); } this.total = orderItemTotal; } return total; } /** * Adds the {@link OrderItem} to this {@link Order}. * * @param orderItem * the {@link OrderItem} to add. Must not be null. */ public void addItem(OrderItem orderItem) { if (orderItem == null) { throw new IllegalArgumentException("orderItem must not be null"); } if (this.items.add(orderItem)) { /* * the list of order items changed so we reset the total field to * let getTotal re-calculate the total. */ this.total = null; } } /** * * @return the {@link OrderItem} that belong to this {@link Order}. Clients * may not modify the returned {@link List}. Use * {@link #addItem(OrderItem)} instead. */ public List<OrderItem> getItems() { /* * we wrap our items to prevent clients from manipulating our internal * state. */ return Collections.unmodifiableList(items); } } public class OrderItem { private BigDecimal price; private int quantity; private String name = "no name"; public OrderItem(BigDecimal price, int quantity, String name) { if (price == null) { throw new IllegalArgumentException("price must not be null"); } if (name == null) { throw new IllegalArgumentException("name must not be null"); } if (price.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException( "price must be a positive big decimal"); } if (quantity < 1) { throw new IllegalArgumentException("quantity must be 1 or greater"); } this.price = price; this.quantity = quantity; this.name = name; } public BigDecimal getPrice() { return price; } public int getQuantity() { return quantity; } public String getName() { return name; } /** * The total is defined as the {@link #getPrice()} multiplied with the * {@link #getQuantity()}. * * @return */ public BigDecimal getTotal() { int quantity = getQuantity(); BigDecimal price = getPrice(); BigDecimal total = price.multiply(new BigDecimal(quantity)); return total; } }

El modelo de dominio enriquecido respeta los principios orientados a objetos y las garantías de que se encuentra en un estado legal en cualquier momento.

Referencias


Simplemente viola el principio de "Diga, no pregunte" , que establece que los objetos deben decirle al cliente lo que pueden o no pueden hacer en lugar de exponer las propiedades y dejarlo en manos del cliente para determinar si un objeto está en un estado particular durante un tiempo determinado. Dada la acción para que tenga lugar.