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 Bar
usando y qué campos serían nulos y cuáles no.
¿Qué otras opciones tengo?
Dos clases llamadas BarPreview
y Bar
?
¿Algún tipo de herencia / interfaz?
¿Algo más que es asombroso?
Bar
como 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
Bar
claseEl 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
HawaiianPizzaBuilder
funciona 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? ElHawaiianPizzaBuilder
todavía tendría todos los captadores queSpicyPizzaBuilder
es tan nulo posible. A menos que combine esto con @JarrodsNull Object Pattern
. Un ejemplo de código con loBar
harí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 Pattern
misma 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 Value
denull. It doesn't replace the semantic of
nulobut makes it easier to work with the data model in a more predictable way because
nulo` 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
null
opción como aDefault Value
. De esta manera, no tiene que verificarnull
, puede verificar la identidad con suNullObject
instancia conocida . Puede invocar métodos seguros y similares sin preocuparseNullPointerExceptions
.De esta manera, reemplaza sus
null
tareas con susNullObject
instancias representativas y listo.Análisis orientado a objetos adecuado
De esta manera, puede tener un común
Interface
para 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, algunosBar
pueden no tener presencia en la web y otros pueden no tener datos de ubicación en el momento de la construcción.Null Object Patter
le permite proporcionar un valor predeterminado para cada uno de estosmarker
datos que dice lo mismo, no se ha proporcionado nada aquí, sin tener que lidiar con la verificación deNullPointerException
todo el lugar.Diseño orientado a objetos adecuado
Primero tenga una
abstract
implementación que sea un súper conjunto de todos los atributos que compartenBar
yClub
comparten.Luego puede implementar subclases de esta
Establishment
clase y agregar solo las cosas específicas que necesita para cada una de las clasesBar
yClub
que 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
. Recordarnull
significa la ausencia de datos , no la ausencia del atributo.Mi respuesta para cualquiera con este tipo de problema es dividirlo en pasos manejables .
BarOne
yBarTwo
(o llámalos a ambosBar
pero en paquetes diferentes)bar
cambian el nombre de la clase infractora a lo que ahora representainterface
osuperclass
con el comportamiento comúninterface
osuperclass
podría crear unbuilder
ofactory
para 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
.final
los campos @David. aunqueBar
no deberíaextend Location
tener sentido. Tal vezBar extends BaseBar
yBarPreview extends BaseBar
estos nombres tampoco suenan demasiado bien, esperaba algo más elegante