Eu tenho dois objetos que representam um 'Bar / Club' (um lugar onde você bebe / socializa).
Em um cenário, preciso do nome da barra, endereço, distância, slogon
Em outro cenário, preciso do nome da barra, endereço, URL do site, logotipo
Então, eu tenho dois objetos representando a mesma coisa, mas com campos diferentes.
Eu gosto de usar objetos imutáveis, então todos os campos são definidos pelo construtor .
Uma opção é ter dois construtores e anular os outros campos, ou seja:
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
}
Eu não gosto disso, pois você precisaria verificar nulo quando usar os getters
No meu exemplo real, o primeiro cenário possui 3 campos e o segundo cenário, cerca de 10; portanto, seria uma dor real ter dois construtores , a quantidade de campos que eu precisaria declarar nula e, quando o objeto estiver em uso, você não você não sabe qual Bar
você está usando e quais campos seriam nulos e quais não.
Que outras opções eu tenho?
Duas classes chamadas BarPreview
e Bar
?
Algum tipo de herança / interface?
Outra coisa que é incrível?
Bar
como identificador!You should only ask practical, answerable questions based on actual problems that you face.
E isso é exatamente o que está acontecendo aquiRespostas:
Meus pensamentos:
Uma "barra", como representada no seu domínio, possui tudo o que é necessário em qualquer lugar: nome, endereço, URL, logotipo, slogan e "distância" (suponho que seja da localização do solicitante). Portanto, em seu domínio, deve haver uma classe "Bar" que seja a fonte autorizada de dados para uma barra, independentemente de onde os dados serão usados posteriormente. Essa classe deve ser mutável, para que alterações nos dados da barra possam ser feitas e salvas quando necessário.
No entanto, você tem dois locais nos quais os dados desse objeto Bar são necessários e os dois precisam apenas de um subconjunto (e você não deseja que esses dados sejam alterados). A resposta usual é um "objeto de transferência de dados" ou DTO; um POJO (objeto simples do Java) contendo os getters de propriedades imutáveis. Esses DTOs podem ser produzidos chamando um método no objeto principal do domínio Bar: "toScenario1DTO ()" e "toScenario2DTO ()"; os resultados são um DTO hidratado (o que significa que você só precisa usar o construtor longo e complicado em um único local).
Se você precisar enviar dados de volta para a classe de domínio principal (para atualizá-los; qual é o sentido dos dados se você não puder alterá-los conforme necessário para refletir o estado atual do mundo real?), Poderá criar um dos seguintes DTOs ou use um novo DTO mutável e devolva-o à classe Bar usando o método "updateFromDto ()".
EDIT: para fornecer um exemplo:
As classes Endereço, Distância e URL devem ser imutáveis ou, quando usadas nos DTOs, devem ser substituídas por equivalentes imutáveis.
fonte
Se você se importa apenas com um subconjunto das propriedades e deseja garantir que elas não se misturem, crie duas interfaces e use isso para conversar com seu objeto base.
fonte
Bar
classeO Padrão do Construtor (ou algo parecido com ele) pode ser útil aqui.
Ter objetos imutáveis é algo admirável, mas a realidade é que, com o Reflection em Java, nada é verdadeiramente seguro ;-).
fonte
HawaiianPizzaBuilder
funciona, porque os valores de que ele precisa são codificados. No entanto, como você pode usar esse padrão se os valores são recuperados e passados para um construtor? OHawaiianPizzaBuilder
ainda teria todos os getters que oSpicyPizzaBuilder
nulo é possível. A menos que você combine isso com @JarrodsNull Object Pattern
. Um exemplo de código comBar
o seu ponto de vistaO ponto chave aqui é a diferença entre o que é uma "barra" e como você a usa em um ou outro contexto.
O Bar é uma entidade única no mundo real (ou um mundo artificial, como um jogo), e apenas UMA instância de objeto deve representá-lo. A qualquer momento, quando você não criar essa instância a partir de um segmento de código, mas carregá-la de um arquivo de configuração ou de um banco de dados, isso será mais evidente.
(Para ser ainda mais esotérico: cada instância do Bar tem um ciclo de vida diferente do objeto que o representa quando o programa é executado. Mesmo se você tiver um código-fonte que cria essa instância, significa que a entidade do Bar, conforme descrita, "existe "em um estado inativo no seu código-fonte e" desperte "quando esse código realmente o criar na memória ...)
Desculpe pelo longo começo, mas espero que isso esclareça meu argumento. Você tem uma classe Bar com todos os atributos que você precisaria e uma instância Bar representando cada entidade Bar. Isso está correto no seu código e independente de como você deseja ver a mesma instância em diferentes contextos .
O último pode ser representado por duas interfaces diferentes , que contêm os métodos de acesso necessários (getName (), getURL (), getDistance ()), e a classe Bar deve implementar os dois. (E talvez a "distância" mude para "local" e getDistance () se torne um cálculo a partir de outro local :-))
Mas a criação é para a entidade Bar e não para a maneira como você deseja usar essa entidade: um construtor, todos os campos.
EDITADO: Eu posso escrever código! :-)
fonte
Padrão apropriado
O que você está procurando é mais comumente chamado de
Null Object Pattern
. Se você não gostar do nome, pode chamá-lo deUndefined Value Pattern
, o mesmo rótulo semântico diferente. Às vezes, esse padrão é chamadoPoison Pill Pattern
.Em todos esses casos, o objeto é uma substituição ou substitui um nulo em
Default Value
vez denull. It doesn't replace the semantic of
nulo nulobut makes it easier to work with the data model in a more predictable way because
agora nunca deve ser um estado válido.É um padrão em que você reserva uma instância especial de uma determinada classe para representar uma
null
opção diferente como aDefault Value
. Dessa forma, você não precisa verificarnull
, pode verificar a identidade em suaNullObject
instância conhecida . Você pode chamar métodos com segurança e afins sem se preocuparNullPointerExceptions
.Dessa forma, você substitui suas
null
atribuições pelasNullObject
instâncias representativas e está pronto.Análise Orientada a Objetos Adequada
Dessa forma, você pode ter um
Interface
polimorfismo comum e ainda ter proteção contra preocupações com a ausência de dados nas implementações específicas da interface. Portanto, algunsBar
podem não ter presença na Web e outros podem não ter dados de localização no momento da construção.Null Object Patter
permite que você forneça um valor padrão para cada um dessesmarker
dados, que diz a mesma coisa, nada foi fornecido aqui, sem ter que lidar com a verificação deNullPointerException
todos os lugares.Design Orientado a Objetos Adequado
Primeiro ter uma
abstract
implementação que é um conjunto de super de todos os atributos que tantoBar
eClub
ação.Em seguida, você pode implementar subclasses dessa
Establishment
classe e adicionar apenas as coisas específicas necessárias para cada uma das classesBar
eClub
que não se aplicam à outra.Persistência
Esses objetos de espaço reservado, se construídos corretamente, podem ser armazenados de forma transparente em um banco de dados sem nenhum tratamento especial também.
Prova futura
Se você decidiu pular na onda de Inversão de Controle / Injeção de Dependência mais adiante, esse padrão facilita a injeção desses objetos marcadores.
fonte
Eu acho que o problema é que você não está modelando uma barra em nenhum desses cenários (e você está modelando dois problemas diferentes, objetos etc.). Se eu vir uma classe Bar, esperaria alguma funcionalidade relacionada a bebidas, menus, assentos disponíveis e seu objeto não tem nada disso. Se eu vir o comportamento dos seus objetos, você está modelando informações sobre um estabelecimento. Bar é o que você está usando para esse momento, mas não é o comportamento intrinsecamente que eles estão implementando. (Em outro contexto: se você estiver modelando um casamento, terá duas variáveis de instância Pessoa esposa; Pessoa marido; esposa é a função atual que você está atribuindo a esse objeto naquele momento, mas o objeto ainda é uma Pessoa). Eu faria algo assim:
fonte
Realmente não há necessidade de complicar demais isso. Você precisa de dois tipos diferentes de objetos? Faça duas aulas.
Se você precisar operar neles igualmente, considere adicionar uma interface ou usar reflexão.
fonte
null
. Lembrarnull
significa a ausência de dados , não a ausência do atributo.Minha resposta para qualquer pessoa com esse tipo de problema é dividi-lo em etapas gerenciáveis .
BarOne
eBarTwo
(ou chame as duas,Bar
mas em pacotes diferentes)bar
renomeie a classe ofensiva para o que ela representa agorainterface
ousuperclass
com o comportamento comuminterface
ousuperclass
você pode criar umbuilder
oufactory
para criar / recuperar seus objetos de implementação(4 e 5 são sobre o que as outras respostas a esta pergunta são)
fonte
Você precisa de uma classe base, por exemplo, Local que tenha um nome e um endereço . Agora, você tem duas classes Bar e BarPreview estender a classe base de Localização . Em cada classe, você inicializa as variáveis comuns da superclasse e, em seguida, suas variáveis exclusivas:
E semelhante para a classe BarPreview ..
Se você dormir melhor à noite, substitua todas as instâncias de Location no meu código por AnythingYouThinkWouldBeAnAproprNameNameForAThingThatABarExtendsSuchAsFoodServicePlace - ffs.
fonte
final
.final
dos campos @ David.Bar
não deveria,extend Location
porém, que isso não faz sentido. TalvezBar extends BaseBar
eBarPreview extends BaseBar
esses nomes realmente não parecer bom demais, quer, porém, eu estava esperando por algo mais elegante