2010-02-25 14 views
26

En nuestra aplicación heredada Java EE, hay muchas clases de objetos de valor (VO) que normalmente contienen solo getters y setters, quizás equals() y hashCode(). Estas son (típicamente) las entidades que se guardarán en el almacenamiento de persistencia. (Para el registro, nuestra aplicación no tiene EJB, aunque podría cambiar en el futuro, y usamos Hibernate para persistir en nuestras entidades). Toda la lógica de negocios para manipular los datos en VO está en clases separadas (no EJB, solo POJOs). Mi mentalidad OO odia esto, ya que creo que las operaciones en una clase dada deberían residir en esa misma clase. Así que tengo un impulso para refactorizar para mover la lógica a los VO relacionados.¿Las entidades de Enterprise Java deberían ser tontas?

Acabo de tener una discusión con un compañero de trabajo que tiene mucha más experiencia en Java EE que yo, y confirmó que las entidades tontas al menos solían ser el camino recomendado. Sin embargo, también ha leído recientemente opiniones que cuestionan la validez de esta postura.

entiendo que hay cuestiones que al menos limitar lo que se puede poner dentro de una clase de entidad:

  • no debería tener dependencia directa de la capa de datos (por ejemplo, código de consulta se debe más bien a entrar en DAOs separadas)
  • si se expone directamente a las capas superiores o para el cliente (por ejemplo, a través de SOAP), puede ser necesario limitar

su interfaz ¿hay razones más válidas no para mover int lógica o mis entidades? O cualquier otra preocupación para tener en cuenta?

+1

¿Las entidades son VO? –

+0

@Pascal Sí - ver mi actualización. –

Respuesta

17

El DTO y VO se supone que se utiliza para transferir datos y no incrusta la lógica. Por otro lado, se supone que los objetos comerciales incorporan algo de lógica.Digo algo, porque siempre hay un equilibrio para encontrar entre lo que pones en los servicios que coordinan la lógica que implica varios objetos de negocio y lo que pones en los objetos de negocio. La lógica típica en los objetos comerciales puede ser la validación, el cálculo de campo u otra operación que afecte solo a un objeto comercial a la vez.

Tenga en cuenta que no he mencionado el término entidad hasta el momento. Las entidades persistentes se popularizaron con ORM y hoy en día tratamos de usar entidades persistentes como objetos comerciales DTO y al mismo tiempo. Es decir, la entidad misma fluye entre capas y niveles, y contiene algo de lógica.

¿Hay alguna razón válida más que para mover la lógica a mis entidades? O cualquier otras preocupaciones a tener en cuenta?

Como usted señaló, todo es una cuestión de dependencias y de lo que expone. Siempre que las entidades sean tontas (cercanas a DTO) pueden aislarse fácilmente en un contenedor dedicado que sirve como API de la capa. Cuanta más lógica pongas en las entidades, más difícil será hacer eso. Presta atención a lo que expones y de lo que dependes (al cargar la clase, el cliente también necesitará tener la clase depend). Esto se aplica a excepciones, jerarquía de herencia, etc.

Solo para dar un ejemplo, tuve un proyecto donde las entidades tenían un método toXml(...) utilizado en la capa empresarial. Como consecuencia, el cliente de las entidades dependía de XML.

Pero si no te preocupan demasiado las capas y la estricta separación entre la API y la implementación, creo que es bueno mover algo de lógica en las entidades.

EDITAR

Esta cuestión se ha discutido muchas veces y probablemente continuará siendo discutido ya que no hay una respuesta definitiva. Algunos enlaces interesantes:

+0

Encontré la mayoría de las respuestas útiles y fue difícil elegir una como "la mejor". Sin embargo, encontré el tuyo más equilibrado y perspicaz, así que obtienes la marca de verificación :-) Gracias. –

+0

Sí, habiendo releído esto más de un año después, tengo que reconocer que ewernli definitivamente conoce bien el tema. –

8

Creo que su punto es válido.
Consulte esto para más información - http://martinfowler.com/bliki/AnemicDomainModel.html
Con JPA, las entidades son objetos livianos. Entonces no creo que sea ningún problema tener lógica en ellos.

En caso de utilizarlo con SOAP/Web Services, agregaría una Fachada de capa por separado.

+3

De acuerdo. Usar objetos con solo getters y setters va completamente en contra de OO y es más como usar structs en C. Me gusta C, pero esto es Java y me estremezco cada vez que tengo que vadear a través de una clase que no contiene nada más que caldera en el formulario de getX() y setX() –

+0

@Padmarag Excelente artículo, gracias :-) –

+1

Ojalá fuera así de simple :) – ewernli

1

La convención a la que se refiere es adoptar un modelo de dominio anémico, en oposición al modelo de dominio enriquecido, donde las entidades son POJO simples anotadas como beans (@Entity) con el mínimo indispensable en términos de getters y setters. Por lo tanto, una clase con un método toXML() se consideraría dominio enriquecido. Supongo que ayuda a mantener una visión clara de lo que se está asignando a una base de datos relacional, incluso si la granularidad es diferente.

La ventaja es que existe una clara separación entre la lógica y los datos (aquí es donde se rompe la filosofía OO). El enfoque es agruparlos en clases de lógica de negocios o Servicios. Esto es de acuerdo con una arquitectura estratificada con capas respectivas que son Dominio, Servicios, DAO y UI.

Esa es la teoría.

Editar: Solo para aclarar, cuando digo que hay una separación clara entre lógica y datos, quiero decir que un objeto está orientado a datos y otro está orientado a métodos de una manera que podría considerarse como un lapso de regreso a procedimientos.

+2

@James P. Me temo que no entiende del todo el objetivo de OO si lo considera "roto" por esto :-( –

+1

Es posible y siempre estoy abierto a la iluminación;). Corrígeme si me equivoco, pero parece que el principio de encapsulación está en el centro del debate aquí. –

+0

P. No estoy seguro de entender dónde está apuntando. De todos modos, no me gustaría "debatir" sobre OO porque a) Preferiría mantener este tema enfocado, b) las opiniones sobre OO son (correctamente) subjetivas y dependientes del contexto, c) 600 caracteres no serían suficientes para esto de todas formas. –

1

La principal p El problema de agregar lógica a esas clases es que necesitarían más atributos para seguir el estado del objeto, y estos atributos adicionales generalmente no necesitan ser serializados. Esto significa que se necesita trabajo adicional en el mecanismo de serialización de esas clases.

Teniendo en cuenta que muchos proyectos tienen una mezcla de jr. programadores y sr. programadores y la mayoría del trabajo es realizado por jr's que no entienden (o se preocupan) por la serialización óptima, es mucho más fácil tener estos simples objetos antiguos de Java como "objetos de valor" que prácticamente pasan y reciben datos y poner la lógica en otro lugar.

Si logras tener una arquitectura donde la lógica se coloca en un objeto comercial (es decir, VO + Lógica), creo que sería mejor también.Solo tenga en cuenta que todo el equipo está en la misma página y no duplican el código y la lógica (lo cual nunca ocurre ¿no?)

Respuesta corta: No, no deberían ser tontos siempre, pero es mucho más fácil tenerlos de esa manera.

+0

Si la serialización es un problema, puede hacer que los atributos adicionales sean transitorios. No es un gran trato. –

+1

@ Mattthew por supuesto, siempre y cuando los novatos comprendan de qué están hablando. – OscarRyz

4

Más allá del artículo de Fowler mencionado anteriormente, hay un tratado completo sobre modelos de dominio rico vs. anémico en el libro de Eric Evans Diseño impulsado por dominio (2004).

Además, la salida http://dddcommunity.org/

+1

Realmente necesita leer este libro. Es uno de esos raros casos en que un trabajo es seminal y útil. – HDave

3

derecho, aquí está un resumen de los comentarios que recibí de mi entrenador Java EE.

Desde un punto de vista práctico, lograr un compromiso entre un dominio anémico y rico moviendo la lógica no debería ser un problema, siempre y cuando los métodos funcionen con los atributos de la Entidad. Cuando se trata de un dominio rico, la línea tiene que dibujarse en alguna parte y aparentemente tanto Fowler como King han emitido comentarios en esa dirección.

Por ejemplo, considere un método calculateInterestRate() dentro de una cuenta de Banco que obtiene información de otros objetos de dominio como verificar cuánto tiempo ha sido un cliente. Para evitar la dependencia, uno podría dividir el método entre objetos, pero este enfoque significa que el código puede terminar siendo distribuido entre varias clases. En este punto, uno también podría hacer una clase InterestCalculator.

Otra cosa a tener en cuenta es la seguridad del hilo. Los DAO y servicios de Singleton manejados por Spring deben ser seguros para subprocesos mientras que cualquier cosa en el modelo de dominio estará expuesta a problemas de concurrencia.

Por último, está el problema del mantenimiento. ¿Estás seguro de que estarás aquí para mantener la aplicación dentro de un par de años? Las elecciones que haga pueden parecer justificadas, pero ¿está seguro de que el próximo desarrollador tendrá la experiencia necesaria para comprender fácilmente su código?

0

He hecho algo de programación en C y aunque OOP es bueno para simplificar problemas mediante el desarrollo de jerarquías de clases, todavía encuentro que el simple C es el más simple y el más grande en muchos casos. En C tenemos estructuras con solo miembros públicos, y el programador transfiere tales estructuras a las funciones (en Java, tales funciones serían, por ejemplo, funciones estáticas en alguna clase de utilidad) que las funciones manipulan a los miembros. Los datos y el algoritmo están separados ya que las funciones no son miembros de las estructuras. Siempre tuve la sensación de que los objetos VO son como estructuras en C.

Hay muchos casos en los que el lenguaje C no es el mejor, porque p. no hay jerarquía, no hay polimorfismo, cosas que los programadores de OOP decentes encuentran útiles. Sin embargo, en general, me gusta este enfoque simple de C y prefiero usarlo a menos que sepa que una técnica de POO sería realmente beneficiosa. P.ej. cuando necesito usar una jerarquía de clases para modelar algo o tengo que asegurarme de que los miembros de una o más clases (en una jerarquía) siempre sean consistentes entre sí, entonces no puedo usar el enfoque C struct. Pero en estos casos, no solo tendría setters y getters de todos modos.

También me refiero este artículo que se trata de C++, pero me encanta cómo este hombre explica tales cosas: http://www.gotw.ca/gotw/084.htm Este artículo tiene 2 reglas acerca de cuándo hacer una función miembro de una clase:

(a partir de la siguiente cita que estoy dejando de lado algunas cosas, leer el original si quieres ver todos)

  1. siempre que sea un miembro de si tiene que ser uno: ¿Qué operaciones deben ser miembros, debido a que la El lenguaje C++ simplemente lo dice (p. Ej., C instructores) o por razones funcionales (p. ej., deben ser virtuales)? Si tienen que serlo, entonces bueno, simplemente tienen que serlo; caso cerrado.

  2. Prefiere ser miembro si necesita acceso a las partes internas: ¿Qué operaciones necesitan acceder a los datos internos que de otro modo tendríamos que otorgar a través de la amistad? Estos deberían ser normalmente miembros.

En todos los demás casos, prefieren que sea un no-amigo no miembro: las operaciones que pueden funcionar igual de bien como nonfriends que no son miembros? Estos pueden, y por lo tanto normalmente deberían, ser no miembros. Este debería ser el caso predeterminado para luchar.

Tengo la sensación de que si tiene dudas sobre si desea agregar funciones a estas clases, no tiene la necesidad real de hacerlo. Lo que escribí aquí es solo una parte de todas las razones por las que no agregaría métodos a tales clases, sin embargo, tal vez este sea el padre de todas mis otras razones. Pero esto es todo subjetivo así que YMMV. Por cierto, el enfoque de funciones de utilidad estáticas hace que las pruebas unitarias sean simples en la mayoría de los casos.

0

Las entidades se generan con frecuencia. En idiomas como Java, no existe una clase parcial, por lo que no podemos extender una entidad (sin considerar patrones como Visitor). Entonces, cuando necesite regenerar sus entidades, deberá agregar nuevamente la lógica comercial, lo que no es práctico en absoluto.

Con las entidades preferiría ir a un diseño high cohesion entre las entidades y su lógica.

Otros aspectos a tener en cuenta son el hecho de que no todas las operaciones comerciales en una entidad son iguales en todas las situaciones. Además, permitir la lógica de negocios en las entidades tiende a someter a la entidad a un acoplamiento con tecnologías de integración como XML que, en mi opinión, deberían mantenerse alejadas de la lógica de dominio en todo momento. En una arquitectura de cebolla, XML estaría en el caparazón externo y los objetos de dominio en el interior, para ilustrar cuán lejos realmente se eliminan entre sí si desea crear un sistema enchufable reutilizable.

Cuestiones relacionadas