Mam class Car
co ma 2 właściwości: int price
i boolean inStock
. Posiada również List
od abstract class State
(pustej klasie). Istnieją dwa stany, które można zastosować w samochodzie i każdy jest reprezentowany przez własną klasę: class Upgrade extends State
i class Shipping extends State
.
A Car
może pomieścić dowolną liczbę każdego z 2 stanów. Stany mają następujące zasady:
Upgrade
: dodaje1
się do ceny za każdy stan zastosowany do samochodu po sobie.Shipping
: jeśliShipping
na liście jest co najmniej 1 stan, toinStock
jest ustawione nafalse
.
Na przykład zaczynając od price = 1
i 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
Myślałem o wzorcu obserwatora, w którym każda operacja dodawania i usuwania powiadamia obserwatorów. Miałem na myśli coś takiego, ale to nie jest zgodne z zasadami, które ustanowiłem:
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);
}
}
Oczywiście to nie działa. Kiedy a Shipping
jest usuwane, coś musi sprawdzić, czy istnieje inne ustawienie stanu inStock
na false, więc usunięcie Shipping
nie może po prostu inStock = true
. Upgrade
zwiększa się price
przy każdym połączeniu. Następnie dodałem stałe wartości domyślnych i spróbowałem ponownie je obliczyć.
W żadnym wypadku nie próbuję narzucać żadnego wzorca, staram się tylko znaleźć rozwiązanie dla powyższych wymagań. Należy zauważyć, że w praktyce Car
zawiera wiele właściwości i istnieje wiele stanów, które można zastosować w ten sposób. Pomyślałem o kilku sposobach:
- Ponieważ każdy obserwator odbiera
Car
, może patrzeć na wszystkich innych aktualnie zarejestrowanych obserwatorów i na tej podstawie dokonać zmiany. Nie wiem, czy mądrze jest uwikłać takich obserwatorów. - Po dodaniu lub usunięciu obserwatora
Car
nastąpi ponowne obliczenie. Jednak to ponowne obliczenie będzie musiało zostać wykonane na wszystkich obserwatorach, niezależnie od tego, który został właśnie dodany / usunięty. - Mieć zewnętrzną klasę „manager”, która wywoła metody dodawania i usuwania i wykonuje ponowne obliczenia.
Jaki jest dobry wzorzec projektowy do wdrożenia opisanego zachowania i jak by to działało?
źródło
Odpowiedzi:
Obserwatorzy działaliby świetnie, gdybyś inaczej rozłożył system. Zamiast uczynić państwa samymi obserwatorami, można utworzyć 2 nowe klasy, które będą „obserwatorami zmiany stanu”: jeden obserwator zaktualizuje „cenę”, a drugi zaktualizuje „inStock”. W ten sposób będą one niezależne, jeśli nie będziesz mieć reguł dotyczących ceny w zależności od inStock lub odwrotnie, tj. Jeśli wszystko można obliczyć, patrząc tylko na zmiany stanu. Ta technika nazywa się „pozyskiwaniem zdarzeń” (na przykład patrz: https://ookami86.github.io/event-sourcing-in-practice/ ). Jest to wzorzec w programowaniu, który ma kilka znaczących aplikacji.
Odpowiadając na bardziej ogólne pytanie, czasami naprawdę masz zależności między obserwatorami. Na przykład możesz chcieć, aby jeden obserwator zareagował przed drugim. W takim przypadku zazwyczaj możliwe jest wykonanie niestandardowej implementacji klasy Observable w celu uporządkowania zamówień lub zależności.
źródło
Skończyło się na opcji 3 - przy użyciu zewnętrznego menedżera. Menedżer jest odpowiedzialny za dodawanie i usuwanie
State
S zCar
S i powiadamiania obserwatorów, kiedy te zmiany zachodzą.Oto jak zmodyfikowałem kod. Usunąłem
Observable
/Observer
z JDK, ponieważ wykonuję własną implementację.Każdy
State
zachowuje odniesienie doCar
zastosowanego.Car
zachowuje tylko swój stan (aby uniknąć pomyłek: właściwości) i nie obsługuje dodawania i usuwaniaState
s:Oto kierownik. Wyprzedził
Car
(obserwowalny) obowiązek zarządzania swoimiState
(obserwatorami).Kilka rzeczy, o których należy pamiętać:
update
metody obserwatora .update
metodęupdateOnAdd
iupdateOnRemove
jeśli są zainteresowani tylko jedną z tych zmian. Następnie metodyaddState
iremoveState
zostaną odpowiednio zaktualizowane. Wraz z poprzednim punktem podejście to może stać się solidnym, rozszerzalnym i elastycznym mechanizmem.State
s, a kiedy tak się dzieje, ponieważ nie ma to znaczenia dla pytania. Jednak w przypadku tej odpowiedzi należy rozważyć następujące kwestie. PonieważState
teraz musi zostać utworzony z jegoCar
(bez ujawnienia pustego konstruktora) przed wywołaniem metody menedżera, metodyaddState
iremoveState
nie muszą braćCar
i mogą po prostu odczytaćstate.car
.źródło