J'ai un class Car
qui a 2 propriétés: int price
et boolean inStock
. Il contient également un List
of abstract class State
(classe vide). Il y a 2 états qui peuvent être appliqués sur la voiture et chacun est représenté par sa propre classe: class Upgrade extends State
et class Shipping extends State
.
A Car
peut contenir n'importe quel nombre de chacun des 2 états. Les États ont les règles suivantes:
Upgrade
: ajoute1
au prix de chaque état appliqué à la voiture après elle-même.Shipping
: s'il y a au moins 1Shipping
état dans la liste, alorsinStock
est réglé surfalse
.
Par exemple, en commençant par price = 1
et inStock = true
:
add Shipping s1 --> price: 1, inStock: false
add Upgrade g1 --> price: 1, inStock: false
add Shipping s2 --> price: 2, inStock: false
add Shipping s3 --> price: 3, inStock: false
remove Shipping s2 --> price: 2, inStock: false
remove Upgrade g1 --> price: 1, inStock: false
remove Shipping s1 --> price: 1, inStock: false
remove Shipping s3 --> price: 1, inStock: true
Je pensais au modèle d'observateur où chaque opération d'ajout et de suppression avertit les observateurs. J'avais quelque chose comme ça en tête, mais ça n'obéit pas aux règles que j'ai posées:
abstract class State implements Observer {
public abstract void update();
}
class Car extends Observable {
List<State> states = new ArrayList<>();
int price = 100;
boolean inStock = true;
void addState(State state) {
if (states.add(state)) {
addObserver(state);
setChanged();
notifyObservers();
}
}
void removeState(State state) {
if (states.remove(state)) {
deleteObserver(state);
setChanged();
notifyObservers();
}
}
}
class Upgrade extends State {
@Override
public void update(Observable o, Object arg) {
Car c = (Car) o;
int bonus = c.states.size() - c.states.indexOf(this) - 1;
c.price += bonus;
System.out.println(c.inStock + " " + c.price);
}
}
class Shipping extends State {
@Override
public void update(Observable o, Object arg) {
Car c = (Car) o;
c.inStock = false;
System.out.println(c.inStock + " " + c.price);
}
}
De toute évidence, cela ne fonctionne pas. Quand a Shipping
est supprimé, quelque chose doit vérifier s'il y a un autre paramètre d'état inStock
sur false, donc une suppression de Shipping
ne peut pas simplement inStock = true
. Upgrade
augmente price
à chaque appel. J'ai ensuite ajouté des constantes pour les valeurs par défaut et tenté un recalcul basé sur celles-ci.
Je n'essaie en aucun cas d'imposer un modèle, j'essaie simplement de trouver une solution pour les exigences ci-dessus. Notez qu'en pratique, il Car
contient de nombreuses propriétés et que de nombreux états peuvent être appliqués de cette manière. J'ai réfléchi à quelques façons de procéder:
- Étant donné que chaque observateur reçoit
Car
, il peut regarder tous les autres observateurs actuellement enregistrés et effectuer un changement en fonction de cela. Je ne sais pas s'il est intelligent d'enchevêtrer des observateurs comme celui-ci. - Lorsqu'un observateur est ajouté ou supprimé
Car
, il y aura un nouveau calcul. Cependant, ce recalcul devra être effectué sur tous les observateurs indépendamment de celui qui vient d'être ajouté / supprimé. - Avoir une classe "manager" externe qui appellera les méthodes add et remove et fera le recalcul.
Qu'est-ce qu'un bon modèle de conception pour implémenter le comportement décrit et comment cela fonctionnerait-il?
la source
Réponses:
Les observateurs fonctionneraient très bien si vous factorisez le système différemment. Au lieu de faire des États eux-mêmes des observateurs, vous pourriez faire de 2 nouvelles classes des "observateurs des changements d'état": un observateur mettrait à jour "prix", un autre mettrait à jour "inStock". De cette façon, ils seraient indépendants si vous n'avez pas de règles de prix en fonction de InStock ou vice versa, c'est-à-dire si tout pouvait être calculé en regardant simplement les changements d'état. Cette technique est appelée "sourcing d'événements" (voir par exemple - https://ookami86.github.io/event-sourcing-in-practice/ ). C'est un modèle de programmation qui a des applications notables.
En réponse à une question plus générale, parfois vous avez vraiment des dépendances entre observateurs. Par exemple, vous pouvez souhaiter qu'un observateur réagisse avant un autre. Dans ce cas, il est généralement possible de faire une implémentation personnalisée de la classe Observable pour gérer l'ordre ou les dépendances.
la source
J'ai fini par choisir l'option 3 - en utilisant un gestionnaire externe. Le gestionnaire est chargé d'ajouter et de supprimer des
State
s deCar
s et d'aviser les observateurs lorsque ces changements se produisent.Voici comment j'ai modifié le code. J'ai supprimé le
Observable
/Observer
du JDK car je fais ma propre implémentation.Chacun
State
garde une référence àCar
son appliqué.Car
ne conserve que son état (pour éviter toute confusion: propriétés) et ne gère pas l'ajout et la suppression deState
s:Voici le manager. Il a dépassé
Car
le travail (observable) de gestion de sesState
(observateurs).Quelques points à garder à l'esprit:
update
méthode des observateurs .update
méthode actuelleupdateOnAdd
etupdateOnRemove
s'ils ne sont intéressés que par l'une de ces modifications. Ensuite, les méthodesaddState
etremoveState
seraient mises à jour en conséquence. Parallèlement au point précédent, cette approche peut devenir un mécanisme robuste, extensible et flexible.State
s et quand cela se produit car ce n'est pas important pour la question. Cependant, dans le cas de cette réponse, il y a le point suivant à considérer. CommeState
maintenant doit être créé avec sonCar
(aucun constructeur vide exposé) avant d'appeler la méthode du gestionnaire, les méthodesaddState
etremoveState
n'ont pas besoin de prendre unCar
et peuvent simplement le lirestate.car
.la source