Ich habe eine, class Car
die 2 Eigenschaften hat: int price
und boolean inStock
. Es enthält auch eine List
von abstract class State
(leere Klasse). Es gibt 2 Zustände, die auf das Auto angewendet werden können und jeder wird durch seine eigene Klasse dargestellt: class Upgrade extends State
und class Shipping extends State
.
A Car
kann eine beliebige Anzahl der beiden Zustände enthalten. Die Staaten haben folgende Regeln:
Upgrade
: erhöht1
den Preis für jeden Zustand, der auf das Auto nach sich selbst angewendet wird.Shipping
: WennShipping
die Liste mindestens 1 Status enthält,inStock
wird auf gesetztfalse
.
Zum Beispiel beginnend mit price = 1
und 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
Ich dachte über das Beobachtermuster nach, bei dem jeder Vorgang zum Hinzufügen und Entfernen Beobachter benachrichtigt. Ich hatte so etwas im Sinn, aber es entspricht nicht den Regeln, die ich aufgestellt habe:
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);
}
}
Offensichtlich funktioniert das nicht. Wenn a Shipping
entfernt wird, muss etwas prüfen, ob es eine andere Statuseinstellung inStock
auf false gibt, sodass ein Entfernen von Shipping
nicht einfach ist inStock = true
. Upgrade
erhöht sich price
bei jedem Anruf. Ich habe dann Konstanten für die Standardwerte hinzugefügt und eine Neuberechnung basierend auf diesen versucht.
Ich versuche keineswegs, ein Muster aufzuerlegen, ich versuche nur, eine Lösung für die oben genannten Anforderungen zu finden. Beachten Sie, dass in der Praxis Car
viele Eigenschaften enthalten und es viele Zustände gibt, die auf diese Weise angewendet werden können. Ich dachte über ein paar Möglichkeiten nach:
- Da jeder Beobachter empfängt
Car
, kann er alle anderen derzeit registrierten Beobachter anzeigen und darauf basierend eine Änderung vornehmen. Ich weiß nicht, ob es klug ist, solche Beobachter zu verwickeln. - Wenn ein Beobachter hinzugefügt oder entfernt wird
Car
, erfolgt eine Neuberechnung. Diese Neuberechnung muss jedoch für alle Beobachter durchgeführt werden, unabhängig davon, welcher gerade hinzugefügt / entfernt wurde. - Haben Sie eine externe "Manager" -Klasse, die die Methoden zum Hinzufügen und Entfernen aufruft und die Neuberechnung durchführt.
Was ist ein gutes Entwurfsmuster, um das beschriebene Verhalten zu implementieren, und wie würde es funktionieren?
quelle
Antworten:
Die Beobachter würden großartig funktionieren, wenn Sie das System anders faktorisieren. Anstatt Staaten selbst zu Beobachtern zu machen, könnten Sie zwei neue Klassen zu "Beobachtern von Zustandsänderungen" machen: Ein Beobachter würde "Preis" aktualisieren, ein anderer würde "inStock" aktualisieren. Auf diese Weise wären sie unabhängig, wenn Sie keine Preisregeln in Abhängigkeit von inStock haben oder umgekehrt, dh wenn alles berechnet werden könnte, indem Sie nur die Statusänderungen betrachten. Diese Technik wird als "Event Sourcing" bezeichnet (siehe z. B. https://ookami86.github.io/event-sourcing-in-practice/ ). Es ist ein Muster in der Programmierung, das einige bemerkenswerte Anwendungen hat.
Bei der Beantwortung einer allgemeineren Frage haben Sie manchmal wirklich Abhängigkeiten zwischen Beobachtern. Beispielsweise möchten Sie möglicherweise, dass ein Beobachter vor einem anderen reagiert. In diesem Fall ist es normalerweise möglich, eine benutzerdefinierte Implementierung der Observable-Klasse zu erstellen, um die Reihenfolge oder Abhängigkeiten zu berücksichtigen.
quelle
Am Ende habe ich Option 3 gewählt - mit einem externen Manager. Der Manager ist dafür verantwortlich,
State
s zuCar
s hinzuzufügen und daraus zu entfernen und die Beobachter zu benachrichtigen, wenn diese Änderungen eintreten.Hier ist, wie ich den Code geändert habe. Ich habe das
Observable
/Observer
des JDK entfernt, weil ich meine eigene Implementierung mache.Jeder
State
behält einen Verweis auf das, auf das erCar
angewendet wird.Car
behält nur seinen Status bei (um Verwirrung zu vermeiden: Eigenschaften) und behandelt nicht das Hinzufügen und Entfernen vonState
s:Hier ist der Manager. Es hat die
Car
(beobachtbare) Aufgabe der Verwaltung seinerState
(Beobachter) übernommen.Ein paar Dinge zu beachten:
update
.update
Methode aufteilenupdateOnAdd
undupdateOnRemove
wenn sie nur an einer dieser Änderungen interessiert sind. Dann würden die MethodenaddState
undremoveState
entsprechend aktualisiert. Zusammen mit dem vorherigen Punkt kann dieser Ansatz als robuster, erweiterbarer und flexibler Mechanismus enden.State
s enthält und wann dies geschieht, da dies für die Frage nicht wichtig ist. Im Fall dieser Antwort ist jedoch der folgende Punkt zu berücksichtigen. DaState
jetztCar
vor dem Aufrufen der Manager-Methode mit (kein leerer Konstruktor verfügbar gemacht) erstellt werden muss, müssen die MethodenaddState
undremoveState
kein a nehmenCar
und können es einfach lesenstate.car
.quelle