tutorial lesson java oop domain-driven-design

java - lesson - ¿Es una buena práctica usar objetos de dominio como claves?



oracle java swing tutorial (13)

¿Es una buena práctica usar objetos de dominio como claves para mapas (u métodos de "obtención"), o es mejor usar solo el id del objeto de dominio?

Es más sencillo de explicar con un ejemplo. Digamos que tengo una clase de persona, una clase de club y una clase de membresía (que conecta las otras dos). Es decir,

public class Person { private int id; // primary key private String name; } public class Club { private String name; // primary key } public class Membership { private Person person; private Club club; private Date expires; }

O algo así. Ahora, quiero agregar un método getMembership to Club. La pregunta es, si este método toma un objeto Person:

public Membership getMembership(Person person);

o, el id de una persona:

public Membership getMembership(int personId);

¿Cuál es la más idiomática, la más conveniente, la más adecuada?

Edit: Muchas respuestas muy buenas. Fui sin exponer el ID, porque las "Personas" (como te habrás dado cuenta, mi dominio real no tiene nada que ver con las personas y los clubes ...) Las instancias están disponibles fácilmente, pero por ahora se almacena internamente en un hashap de hashap en el id, pero al menos lo estoy exponiendo correctamente en la interfaz.


A menos que se obtenga un beneficio significativo en otra parte, se puede decir que las claves en el mapa deben ser cosas de un solo valor, si es posible. Dicho esto, al prestar atención a equals () y hashCode () puede hacer que cualquier objeto funcione como una clave, pero equals () y hashCode () no son cosas muy agradables a las que prestar atención. Serás más feliz adhiriéndose a las identificaciones como claves.


Creo que el primer caso se consideraría "más puro" en el sentido de que el método getMembership podría requerir datos más específicos de la propia persona que no sea su id. (Supongamos que no conoce los elementos internos del método getMembership, aunque esto hace poco sentido ya que es más probable en el mismo dominio).

Si resulta que realmente requiere datos de la entidad Persona, entonces no requerirá un DAO o una fábrica para la persona en cuestión.

Puede llamarlo fácilmente si su idioma y / o ORM le permite usar objetos proxy (y si tiene una forma conveniente de crear estos proxies).

Pero seamos honestos. Si está preguntando por alguna membresía de una persona, lo más probable es que ya tenga esta instancia de la Persona en la memoria cuando llame a este método.

Más adelante en el "terreno de la infraestructura" también está esta noción sobre los detalles de la implementación que Uri ya mencionó mientras escribía esta respuesta (¡maldita sea, eso fue rápido!). Para ser más específicos, ¿qué pasaría si decidiera que este concepto de ''Persona'' tiene de repente una clave / identificador principal compuesto en la base de datos subyacente ... ahora usaría una clase de identificador? ¿Quizás usar ese proxy del que estábamos hablando?

TL; versión DR

El uso de ID es realmente más fácil a corto plazo, pero si ya está usando un ORM sólido, no veo ninguna razón para no usar proxies o algún otro medio para expresar la identidad orientada a objetos de una Entidad que no filtra los detalles de implementación.


En mi opinión, creo que depende mucho del flujo de la aplicación. ¿Tiene a la Person disponible cuando desea obtener los detalles de la Membership ? Si es así, vaya con:
public Membership getMembership(Person person);

Además, no veo ninguna razón por la que el Club no pueda realizar un seguimiento de las membresías en función de la ID de la Person y no del objeto real. Creo que eso significaría que no es necesario implementar el hashCode() y equals() metodos (Aunque eso siempre es una buena práctica).

Como dijo Uri, debe documentar la desaceleración de que dos objetos Person son iguales si su ID es igual.


En primer lugar, pondría a cualquier captador de este tipo dentro de un DAO (y no en el modelo). Luego usaría la entidad en sí misma como un parámetro, y lo que sucede dentro del método es un detalle de la implementación.


En realidad, lo que haría es llamarlo por id, pero refactorizando un poco el diseño original:

public class Person { private int id; // primary key private String name; } public class Club { private String name; // primary key private Collection<Membership> memberships; public Membership getMembershipByPersonId(int id); } public class Membership { private Date expires; private Person person; }

o

public class Person { private int id; // primary key private String name; private Membership membership; public Membership getMembership(); } public class Club { private String name; // primary key private Collection<Person> persons; public Person getPersonById(int id); } public class Membership { private Date expires; }


No uses al hombre del id, esto es solo una mala idea por todas las razones mencionadas. Te encerrarás en un diseño. Déjame dar un ejemplo.

En este momento, usted define su membresía como una asignación entre Clubs to People. Con razón, su Membresía debe ser un mapa de los Clubes a "Miembros", pero está asumiendo que todos los Miembros son Personas y que, dado que todas las ID de personas son únicas, usted cree que solo puede usar la ID.

Pero, ¿qué pasa si en el futuro desea extender su concepto de membresía a "membresías familiares", para lo cual crea una mesa familiar y una clase familiar? De buena manera, extrae una interfaz de Familia y Persona llamada Miembro. Mientras ambas clases implementen los métodos equals y hashCode correctamente, no se deberá tocar ningún otro código. Personalmente, habría definido la interfaz de Miembro desde el principio.

public interface Member { } public class Person implements Member { private int id; // primary key private String name; } public class Family implements Member { private int id; private String name; } public class Club { private String name; // primary key } public class Membership { private Member member; private Club club; private Date expires; }

Si ha usado ID en su interfaz, deberá aplicar la singularidad de los valores clave en la tabla cruzada, o mantener dos mapas separados y renunciar a las buenas características de la interfaz polimórfica.

Créame, a menos que esté escribiendo aplicaciones desechables y desechables, debe evitar el uso de ID en su interfaz.


Normalmente me quedo con menos es más. Cuanta menos información se requiera para invocar tu método, mejor. Si conoces el ID, solo necesitas el ID.

Si lo desea, proporcione sobrecargas adicionales que acepten parámetros adicionales, como toda la clase.


Probablemente usaría identificaciones. ¿Por qué? Al tomar identificaciones, estoy haciendo suposiciones más seguras sobre la persona que llama.

Si tengo una identificación, ¿cuánto trabajo cuesta obtener la Persona? Puede ser ''fácil'', pero requiere acceder a un almacén de datos, que es lento ...

Si tengo un objeto de persona, ¿cuánto trabajo cuesta obtener la identificación? Acceso de miembro simple. Rápido y disponible.


Según lo descrito por otros: usar el objeto.

Trabajo en un sistema donde teníamos algún código antiguo que usaba int para representar los identificadores de transacción. ¿Adivina qué? Empezamos a quedarnos sin ids porque usamos int.

Cambiar a largo o BigNumber resultó ser complicado porque la gente se había vuelto muy inventiva con los nombres. Algunos usados

int tranNum

algunos usados

int transactionNumber

algunos usados

int trannNum

(Completa con errores de ortografía).

Algunas personas se pusieron realmente inventivas ...

Fue un desastre y solucionarlo fue una pesadilla. Terminé pasando todo el código manualmente y convirtiéndolo en un objeto TransactionNumber.

Oculta los detalles siempre que sea posible.


Si realmente está practicando el diseño orientado a objetos, entonces desea invocar la idea de ocultar información. Tan pronto como comienza a colgar los tipos de campo interno del objeto de persona en la interfaz pública de los métodos del objeto de membresía, comienza a forzar a los desarrolladores externos (usuarios) de sus objetos a comenzar a aprender todo tipo de información sobre qué es un objeto de persona y cómo está almacenado, y qué tipo de identificación tiene.

Mejor aún, ya que una persona puede tener membresías, ¿por qué no simplemente cuelga el método "getMemberships" en la clase persona? Parece mucho más lógico preguntar a una persona qué membresías tiene, que pedir una "membresía" a qué clubes pertenece una persona determinada ...

Editar : dado que el OP se ha actualizado para indicar que es la membresía en la que está interesado, y no solo como una relación entre Persona y Club, estoy actualizando mi respuesta.

En pocas palabras, la clase de "Club" que estás definiendo, ahora estás pidiendo que te comportes como una "lista de jugadores". Un club tiene una plantilla, no es una lista. Una lista podría tener varias características, incluidas formas de buscar personas que pertenecen al club. Además de buscar a una persona por la identificación de su club, es posible que desee buscarla por SSN, nombre, fecha de ingreso, etc. Para mí, esto dice que hay un método en la clase "Club" llamado getRoster (), que devuelve una estructura de datos que puede buscar todas las personas en el club. Llámalo una colección. Luego, la pregunta es: ¿puede usar los métodos en clases de colecciones preexistentes para satisfacer las necesidades que ha definido hasta ahora, o necesita crear una subclase de colección personalizada para proporcionar el método apropiado para encontrar el registro de membresía?

Debido a que la jerarquía de su clase probablemente está respaldada por una base de datos, y es probable que esté tomando información sobre cómo cargar la información de la base de datos, y no necesariamente desea obtener la colección completa solo para obtener una membresía, es posible que desee crear una nueva clase . Esta clase podría ser llamada como dije "Lista". Obtendría la instancia de la llamada getRoster () en la clase "club". Usted agregaría métodos de "búsqueda" a la clase en función de los criterios de búsqueda que quisiera que fueran información "públicamente disponible" sobre la persona .. nombre, clubID, ID de persona, SSN, etc.

Mi respuesta original solo se aplica si la "membresía" es puramente una relación para indicar a qué clubes pertenecen las personas.


Si ya tiene el objeto, no hay razón para sacar la ID para obtener una clave hash.

Siempre que los ID sean siempre únicos, implemente hashCode () para devolver el ID, e implemente equals () también.

Lo más probable es que cada vez que necesites la Membresía, ya tengas a la Persona, por lo que guarda el código y la confusión más adelante.


Suponiendo que se trata de una ID de base de datos o algo que se usa solo para la indexación (en lugar de algo como un SSN), en un sistema ideal, la presencia de una ID es un detalle de implementación.

Como detalle de implementación, preferiría ocultarlo en la interfaz de otros objetos de dominio. Por lo tanto, la membresía implica, fundamentalmente, individuos en lugar de números.

Por supuesto, me aseguraría de implementar el hashCode y equals() y documentar bien lo que significaban.

En ese caso, documentaría explícitamente que la igualdad de dos objetos Person se determina únicamente en función de la ID . Esta es una propuesta un tanto arriesgada, pero hace que el código sea más legible si se puede asegurar. Me siento más cómodo haciéndolo cuando mis objetos son inmutables, por lo que no terminaría con dos objetos Person con la misma identificación pero con nombres diferentes en la vida del programa.


Whoa Retrocede un segundo aquí. El método getMembership() no pertenece al Club . Pertenece al conjunto de todas las membresías que no has implementado.