J'ai deux objets qui représentent un «Bar / Club» (un endroit où vous buvez / socialisez).
Dans un scénario, j'ai besoin du nom de la barre, de l'adresse, de la distance, du slogon
Dans un autre scénario, j'ai besoin du nom de la barre, de l'adresse, de l'URL du site Web, du logo
J'ai donc deux objets représentant la même chose mais avec des champs différents.
J'aime utiliser des objets immuables, donc tous les champs sont définis à partir du constructeur .
Une option consiste à avoir deux constructeurs et à annuler les autres champs, à savoir:
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
}
Je n'aime pas ça car il faudrait vérifier la valeur nulle lorsque vous utilisez les getters
Dans mon exemple réel, le premier scénario a 3 champs et le deuxième scénario a environ 10, donc ce serait vraiment difficile d'avoir deux constructeurs , la quantité de champs que je devrais déclarer nulle et puis quand l'objet est en cours d'utilisation, vous ne le feriez pas. Bar
Je ne sais pas où vous utilisez et quels champs seraient nuls et lesquels ne le seraient pas.
Quelles autres options ai-je?
Deux classes appelées BarPreview
et Bar
?
Un type d'héritage / interface?
Quelque chose d'autre qui est génial?
Bar
comme identifiant!You should only ask practical, answerable questions based on actual problems that you face.
et c'est exactement ce qui se passe iciRéponses:
Mes pensées:
Un "Bar", tel que représenté dans votre domaine, a toutes les choses qui peuvent être nécessaires dans l'un ou l'autre endroit: nom, adresse, URL, logo, slogan et "distance" (je suppose de l'emplacement du demandeur). Par conséquent, dans votre domaine, il doit y avoir une classe "Bar" qui est la source de données faisant autorité pour une barre, peu importe où les données seront utilisées ultérieurement. Cette classe doit être modifiable, afin que les modifications des données de la barre puissent être apportées et enregistrées si nécessaire.
Cependant, vous disposez de deux emplacements dans lesquels les données de cet objet Bar sont nécessaires, et les deux n'ont besoin que d'un sous-ensemble (et vous ne voulez pas que ces données soient modifiées). La réponse habituelle est un "objet de transfert de données" ou DTO; un POJO (simple objet Java) contenant les getters de propriété immuables. Ces DTO peuvent être produits en appelant une méthode sur l'objet de domaine Bar principal: "toScenario1DTO ()" et "toScenario2DTO ()"; les résultats étant un DTO hydraté (ce qui signifie que vous n'avez besoin d'utiliser que le constructeur long et compliqué en un seul endroit).
Si vous avez déjà eu besoin de renvoyer des données à la classe de domaine principale (pour les mettre à jour; quel est l'intérêt des données si vous ne pouvez pas les modifier selon les besoins pour refléter l'état actuel du monde réel?), Vous pouvez construire l'un des DTO, ou utilisez un nouveau DTO mutable et remettez-le à la classe Bar à l'aide d'une méthode "updateFromDto ()".
EDIT: pour donner un exemple:
Les classes Address, Distance et Url doivent être elles-mêmes immuables ou, lorsqu'elles sont utilisées dans les DTO, elles doivent être remplacées par des homologues immuables.
la source
Si vous ne vous souciez que d'un sous-ensemble des propriétés et que vous voulez vous assurer qu'elles ne se mélangent pas, créez deux interfaces et utilisez-les pour parler à votre objet de base.
la source
Bar
classeLe modèle de générateur (ou quelque chose de proche) pourrait être utile ici.
Avoir des objets immuables est une chose admirable, mais la réalité est qu'avec Reflection en Java, rien n'est vraiment sûr ;-).
la source
HawaiianPizzaBuilder
fonctionne parce que les valeurs dont il a besoin sont codées en dur. Cependant, comment pouvez-vous utiliser ce modèle si les valeurs sont récupérées et transmises à un constructeur? LeHawaiianPizzaBuilder
aurait encore tous les getters que l'SpicyPizzaBuilder
a si nul est possible. Sauf si vous combinez cela avec @JarrodsNull Object Pattern
. Un exemple de code avecBar
ferait passer votre messageLe point clé ici est la différence entre ce qu'est un "Bar" et comment vous l'utilisez dans l'un ou l'autre contexte.
Le Bar est une entité unique dans le monde réel (ou un monde artificiel, comme un jeu), et une seule instance d'objet devrait le représenter. À tout moment plus tard, lorsque vous ne créerez pas cette instance à partir d'un segment de code, mais la chargerez à partir d'un fichier de configuration ou d'une base de données, cela sera plus évident.
(Pour être encore plus ésotérique: chaque instance Bar a un cycle de vie différent de l'objet qui la représente lorsque votre programme s'exécute. Même si vous avez un code source qui crée cette instance, cela signifie que l'entité Bar telle qu'elle est décrite, "existe "dans un état dormant dans votre code source, et" réveiller "lorsque ce code le crée réellement dans la mémoire ...)
Désolé pour le long début, mais j'espère que cela clarifie mon propos. Vous avez UNE classe Bar ayant tous les attributs dont vous auriez besoin et une instance Bar représentant chaque entité Bar. Ceci est correct dans votre code et indépendant de la façon dont vous souhaitez voir la même instance dans différents contextes .
Ce dernier peut être représenté par deux interfaces différentes , qui contiennent les méthodes d'accès requises (getName (), getURL (), getDistance ()), et la classe Bar doit implémenter les deux. (Et peut-être que la "distance" changera en "emplacement", et le getDistance () devient un calcul à partir d'un autre emplacement :-))
Mais la création est pour l'entité Bar et non pour la façon dont vous souhaitez utiliser cette entité: un constructeur, tous les champs.
EDITÉ: je peux écrire du code! :-)
la source
Schéma approprié
Ce que vous recherchez est le plus souvent appelé le
Null Object Pattern
. Si vous n'aimez pas le nom, vous pouvez l'appeler laUndefined Value Pattern
même étiquette sémantique différente. Parfois, ce modèle est appeléPoison Pill Pattern
.Dans tous ces cas, l'objet est un remplacement ou se substituer à un
Default Value
lieu denull. It doesn't replace the semantic of
nullebut makes it easier to work with the data model in a more predictable way because
null` devrait maintenant jamais être un état valide.Il s'agit d'un modèle dans lequel vous réservez une instance spéciale d'une classe donnée pour représenter une autre
null
option en tant que aDefault Value
. De cette façon, vous n'avez pas à vérifiernull
, vous pouvez vérifier l'identité par rapport à votreNullObject
instance connue . Vous pouvez appeler en toute sécurité des méthodes dessus et similaires sans vous en soucierNullPointerExceptions
.De cette façon, vous remplacez vos
null
affectations par leursNullObject
instances représentatives et vous avez terminé.Analyse orientée objet appropriée
De cette façon, vous pouvez avoir un commun
Interface
pour le polymorphisme et toujours avoir une protection contre le fait de devoir vous soucier de l'absence de données dans les implémentations spécifiques de l'interface. Ainsi, certainsBar
peuvent ne pas avoir de présence sur le Web et certains peuvent ne pas avoir de données de localisation au moment de la construction.Null Object Patter
vous permet de fournir une valeur par défaut pour chacun d'entre eux, c'est-à-diremarker
pour ces données qui disent la même chose, rien n'a été fourni ici, sans avoir à gérer la vérificationNullPointerException
partout.Conception orientée objet appropriée
Ayez d'abord une
abstract
implémentation qui est un super ensemble de tous les attributs qui partagentBar
etClub
partagent.Ensuite, vous pouvez implémenter des sous-classes de cette
Establishment
classe et ajouter uniquement les éléments spécifiques dont vous avez besoin pour chacune des classesBar
etClub
qui ne s'appliquent pas à l'autre.Persistance
Ces objets d'espace réservé, s'ils sont construits correctement, peuvent également être stockés de manière transparente dans une base de données sans aucune manipulation particulière.
Future Proof
Si vous avez décidé de sauter plus tard dans le train d'inversion de contrôle / injection de dépendance, ce modèle facilite également l'injection de ces objets marqueurs.
la source
Je pense que le problème est que vous ne modélisez pas une barre dans aucun de ces scénarios (et vous modélisez deux problèmes différents, des objets, etc.). Si je vois un bar de classe, je m'attendrais à certaines fonctionnalités liées aux boissons, aux menus, aux sièges disponibles, et votre objet n'a rien de tout cela. Si je vois le comportement de vos objets, vous modélisez des informations sur un établissement. La barre est ce pour quoi vous les utilisez en ce moment, mais ce n'est pas le comportement intrinsèque qu'ils implémentent. (Dans un autre contexte: si vous modélisez un mariage, vous aurez deux variables d'instance Personne épouse; Personne mari; une femme est le rôle actuel que vous donnez à cet objet à ce moment, mais l'objet est toujours une personne). Je ferais quelque chose comme ça:
la source
Il n'y a vraiment pas besoin de trop compliquer cela. Vous avez besoin de deux types d'objets différents? Faites deux classes.
Si vous devez les exploiter également, envisagez d'ajouter une interface ou d'utiliser la réflexion.
la source
null
. Rappelez-vousnull
signifie l' absence de données , pas l'absence de l'attribut.Ma réponse à toute personne ayant ce type de problème est de la décomposer en étapes gérables .
BarOne
etBarTwo
(ou appelez-les toutes les deuxBar
mais dans des packages différents)bar
sinon renommer la classe incriminée en ce qu'elle représente maintenantinterface
ousuperclass
avec le comportement communinterface
ousuperclass
vous pouvez créer unbuilder
oufactory
pour créer / récupérer vos objets d'implémentation(4 et 5 correspondent aux autres réponses à cette question)
la source
Vous avez besoin d'une classe de base, par exemple, Location qui a un nom et une adresse . Maintenant, vous avez deux classes Bar et BarPreview étendent l' emplacement de la classe de base . Dans chaque classe, vous initialisez les variables communes de la super classe, puis vos variables uniques:
Et similaire pour la classe BarPreview ..
Si cela vous fait mieux dormir la nuit, remplacez toutes les instances de Location dans mon code par AnythingYouThinkWouldBeAnAppropriateNameForAThingThatABarExtendsSuchAsFoodServicePlace - ffs.
la source
final
.final
des champs @David.Bar
ne devrait pasextend Location
penser que cela n'a pas de sens. PeutBar extends BaseBar
- être etBarPreview extends BaseBar
ces noms ne sonnent pas vraiment trop bien non plus, j'espérais quelque chose de plus élégant