Tengo dos objetos que representan un 'Bar / Club' (un lugar donde bebes / socializas).
En un escenario, necesito el nombre de la barra, la dirección, la distancia, el eslogan
En otro escenario, necesito el nombre de la barra, la dirección, la URL del sitio web, el logotipo
Entonces tengo dos objetos que representan la misma cosa pero con diferentes campos.
Me gusta usar objetos inmutables, por lo que todos los campos se establecen desde el constructor .
Una opción es tener dos constructores y anular los otros campos, es decir:
class Bar {
private final String name;
private final Distance distance;
private final Url url;
public Bar(String name, Distance distance){
this.name = name;
this.distance = distance;
this.url = null;
}
public Bar(String name, Url url){
this.name = name;
this.distance = null;
this.url = url;
}
// getters
}
No me gusta esto, ya que tendrías que verificar nulo cuando utilizas los captadores
En mi ejemplo real, el primer escenario tiene 3 campos y el segundo escenario tiene aproximadamente 10, por lo que sería un verdadero problema tener dos constructores , la cantidad de campos que tendría que declarar nulo y luego, cuando el objeto esté en uso, no No sé cuál estaba Barusando y qué campos serían nulos y cuáles no.
¿Qué otras opciones tengo?
Dos clases llamadas BarPreviewy Bar?
¿Algún tipo de herencia / interfaz?
¿Algo más que es asombroso?

Barcomo identificador!You should only ask practical, answerable questions based on actual problems that you face.y eso es exactamente lo que está sucediendo aquíRespuestas:
Mis pensamientos:
Una "Barra", como se representa en su dominio, tiene todas las cosas que pueden ser necesarias en cualquier lugar: nombre, dirección, URL, logotipo, eslogan y "distancia" (supongo que desde la ubicación del solicitante). Por lo tanto, en su dominio, debe haber una clase de "Barra" que sea la fuente autorizada de datos para una barra, sin importar dónde se utilizarán los datos más adelante. Esta clase debe ser mutable, de modo que los cambios en los datos de la barra se puedan realizar y guardar cuando sea necesario.
Sin embargo, tiene dos lugares en los que se necesitan los datos de este objeto Bar, y ambos solo necesitan un subconjunto (y no desea que se modifiquen esos datos). La respuesta habitual es un "objeto de transferencia de datos" o DTO; un POJO (objeto simple de Java) que contiene los captadores de propiedades inmutables. Estos DTO se pueden generar llamando a un método en el objeto de dominio de la barra principal: "toScenario1DTO ()" y "toScenario2DTO ()"; los resultados son un DTO hidratado (lo que significa que solo necesita usar el constructor largo y complicado en un solo lugar).
Si alguna vez necesita enviar datos a la clase de dominio principal (para actualizarlos; ¿cuál es el punto de datos si no puede cambiarlos según sea necesario para reflejar el estado actual del mundo real?), Podría construir uno de los DTO, o use un nuevo DTO mutable, y devuélvalo a la clase Bar usando un método "updateFromDto ()".
EDITAR: para proporcionar un ejemplo:
Las clases Address, Distance y Url deberían ser inmutables o, cuando se usan en los DTO, deberían reemplazarse por sus homólogos inmutables.
fuente
Si solo le importa un subconjunto de propiedades y desea asegurarse de que no se mezclen, cree dos interfaces y úselas para hablar con su objeto base.
fuente
BarclaseEl patrón de construcción (o algo parecido) podría ser útil aquí.
Tener objetos inmutables es algo admirable, pero la realidad es que con Reflection in Java, nada es realmente seguro ;-).
fuente
HawaiianPizzaBuilderfunciona porque los valores que necesita están codificados. Sin embargo, ¿cómo puede usar este patrón si los valores se recuperan y se pasan a un constructor? ElHawaiianPizzaBuildertodavía tendría todos los captadores queSpicyPizzaBuilderes tan nulo posible. A menos que combine esto con @JarrodsNull Object Pattern. Un ejemplo de código con loBarharía entenderEl punto clave aquí es la diferencia entre qué es una "Barra" y cómo se usa en uno u otro contexto.
The Bar es una entidad única en el mundo real (o un mundo artificial, como un juego), y solo UNA instancia de objeto debería representarlo. En cualquier momento posterior, cuando no cree esa instancia desde un segmento de código, pero la cargue desde un archivo de configuración o una base de datos, esto será más evidente.
(Para ser aún más esotérico: cada instancia de Bar tiene un ciclo de vida diferente que el objeto que lo representa cuando se ejecuta su programa. Incluso si tiene un código fuente que crea esa instancia, significa que la entidad de Bar como se describe, "existe "en un estado latente en su código fuente, y" despertar "cuando ese código realmente lo crea en la memoria ...)
Perdón por el largo comienzo, pero espero que esto aclare mi punto. Tiene UNA clase de Bar que tiene todos los atributos que necesitaría y una instancia de Bar que representa cada entidad de Bar. Esto es correcto en su código e independiente de cómo desea ver la misma instancia en diferentes contextos .
Este último puede estar representado por dos interfaces diferentes , que contienen los métodos de acceso requeridos (getName (), getURL (), getDistance ()), y la clase Bar debe implementar ambos. (Y tal vez la "distancia" cambiará a "ubicación", y getDistance () se convierte en un cálculo desde otra ubicación :-))
Pero la creación es para la entidad Bar y no para la forma en que desea utilizar esa entidad: un constructor, todos los campos.
EDITADO: ¡Puedo escribir código! :-)
fuente
Patrón apropiado
Lo que estás buscando se conoce comúnmente como el
Null Object Pattern. Si no le gusta el nombre, puede llamarlo, laUndefined Value Patternmisma semántica etiqueta diferente. A veces se llama este patrónPoison Pill Pattern.En todos estos casos, el Objeto es un reemplazo o un sustituto
Default Valuedenull. It doesn't replace the semantic ofnulobut makes it easier to work with the data model in a more predictable way becausenulo` ahora nunca debería ser un estado válido.Es un patrón en el que reserva una instancia especial de una clase determinada para representar una
nullopción como aDefault Value. De esta manera, no tiene que verificarnull, puede verificar la identidad con suNullObjectinstancia conocida . Puede invocar métodos seguros y similares sin preocuparseNullPointerExceptions.De esta manera, reemplaza sus
nulltareas con susNullObjectinstancias representativas y listo.Análisis orientado a objetos adecuado
De esta manera, puede tener un común
Interfacepara el polimorfismo y aún así tener la protección de tener que preocuparse por la ausencia de datos en las implementaciones específicas de la interfaz. Por lo tanto, algunosBarpueden no tener presencia en la web y otros pueden no tener datos de ubicación en el momento de la construcción.Null Object Patterle permite proporcionar un valor predeterminado para cada uno de estosmarkerdatos que dice lo mismo, no se ha proporcionado nada aquí, sin tener que lidiar con la verificación deNullPointerExceptiontodo el lugar.Diseño orientado a objetos adecuado
Primero tenga una
abstractimplementación que sea un súper conjunto de todos los atributos que compartenBaryClubcomparten.Luego puede implementar subclases de esta
Establishmentclase y agregar solo las cosas específicas que necesita para cada una de las clasesBaryClubque no se aplican a la otra.Persistencia
Estos objetos de marcador de posición si se construyen correctamente se pueden almacenar de forma transparente en una base de datos sin ningún manejo especial también.
Prueba del futuro
Si decidiste subirte al carro de la Inversión de Control / Inyección de Dependencia más adelante, este patrón hace que también sea fácil inyectar estos objetos marcadores.
fuente
Creo que el problema es que no estás modelando una barra en ninguno de esos escenarios (y estás modelando dos problemas diferentes, objetos, etc.). Si veo una barra de clase, esperaría alguna funcionalidad relacionada con las bebidas, los menús, los asientos disponibles y su objeto no tiene nada de eso. Si veo el comportamiento de tus objetos, estás modelando información sobre un establecimiento. Bar es para lo que los estás usando en este momento, pero no es el comportamiento intrínseco que están implementando. (En otro contexto: si está modelando un matrimonio, tendrá dos variables de instancia Persona esposa; Persona esposo; una esposa es el rol actual que le está dando a ese objeto en ese momento, pero el objeto sigue siendo una Persona). Haría algo como esto:
fuente
Realmente no hay necesidad de complicar demasiado esto. ¿Necesitas dos tipos diferentes de objeto? Haz dos clases.
Si necesita operar con ellos por igual, considere agregar una interfaz o usar la reflexión.
fuente
null. Recordarnullsignifica la ausencia de datos , no la ausencia del atributo.Mi respuesta para cualquiera con este tipo de problema es dividirlo en pasos manejables .
BarOneyBarTwo(o llámalos a ambosBarpero en paquetes diferentes)barcambian el nombre de la clase infractora a lo que ahora representainterfaceosuperclasscon el comportamiento comúninterfaceosuperclasspodría crear unbuilderofactorypara crear / recuperar sus objetos de implementación(4 y 5 son de lo que tratan las otras respuestas a esta pregunta)
fuente
Necesita una clase base, por ejemplo, Ubicación que tenga un nombre y una dirección . Ahora, tiene dos clases, Bar y BarPreview, extienden la ubicación de la clase base . En cada clase, inicializa las variables comunes de superclase y luego sus variables únicas:
Y similar para la clase BarPreview.
Si lo hace dormir mejor por la noche, reemplace todas las instancias de Ubicación en mi código con AnythingYouThinkWouldBeAnAppropriateNameForAThingThatABarExtendsSuchAsFoodServicePlace - ffs.
fuente
final.finallos campos @David. aunqueBarno deberíaextend Locationtener sentido. Tal vezBar extends BaseBaryBarPreview extends BaseBarestos nombres tampoco suenan demasiado bien, esperaba algo más elegante