Eu tenho um class Car
que tem 2 propriedades: int price
e boolean inStock
. Ele também contém um List
de abstract class State
(classe vazia). Existem 2 estados que podem ser aplicados no carro e cada um é representado por sua própria classe: class Upgrade extends State
e class Shipping extends State
.
A Car
pode conter qualquer número de cada um dos 2 estados. Os estados têm as seguintes regras:
Upgrade
: aumenta1
o preço de cada estado aplicado ao carro depois dele.Shipping
: se houver pelo menos 1Shipping
estado na lista,inStock
será definido comofalse
.
Por exemplo, começando com price = 1
e 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
Eu estava pensando no padrão de observador em que cada operação de adição e remoção notifica os observadores. Eu tinha algo em mente, mas isso não obedece às regras que eu propus:
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);
}
}
Obviamente, isso não funciona. Quando a Shipping
é removido, algo deve verificar se há outra configuração de estado inStock
como false, portanto, a remoção de Shipping
não pode ser apenas inStock = true
. Upgrade
aumenta price
a cada chamada. Em seguida, adicionei constantes para os valores padrão e tentei um recálculo com base nesses valores.
Não estou tentando impor nenhum padrão, apenas estou tentando encontrar uma solução para os requisitos acima. Observe que, na prática, Car
contém muitas propriedades e há muitos estados que podem ser aplicados dessa maneira. Pensei em algumas maneiras de fazer isso:
- Como cada observador recebe
Car
, ele pode observar todos os outros observadores atualmente registrados e fazer uma alteração com base nisso. Não sei se é inteligente envolver os observadores assim. - Quando um observador é adicionado ou removido
Car
, haverá um recálculo. No entanto, esse recálculo deverá ser feito em todos os observadores, independentemente do que acabou de ser adicionado / removido. - Tenha uma classe "gerenciadora" externa que chamará os métodos de adição e remoção e fará o recálculo.
Qual é um bom padrão de design para implementar o comportamento descrito e como ele funcionaria?
fonte
Respostas:
Os observadores funcionariam muito bem se você fatorar o sistema de maneira diferente. Em vez de tornar os próprios estados observadores, você poderia criar duas novas classes para "observadores de mudanças de estado": um observador atualizaria "preço", outro observaria "inStock". Dessa forma, eles seriam independentes se você não tiver regras de preço, dependendo do inStock ou vice-versa, ou seja, se tudo puder ser calculado apenas observando as mudanças de estado. Essa técnica é chamada "fonte de eventos" (por exemplo, consulte - https://ookami86.github.io/event-sourcing-in-practice/ ). É um padrão na programação que possui alguns aplicativos notáveis.
Respondendo a uma pergunta mais geral, às vezes você realmente tem dependências entre os observadores. Por exemplo, você pode querer que um observador reaja antes de outro. Nesse caso, geralmente é possível fazer uma implementação personalizada da classe Observable para lidar com pedidos ou dependências.
fonte
Acabei optando pela opção 3 - usando um gerente externo. O gerente é responsável por adicionar e remover se
State
de seCar
notificar os observadores quando essas mudanças ocorrerem.Aqui está como eu modifiquei o código. Eu removi o
Observable
/Observer
do JDK porque estou fazendo minha própria implementação.Cada
State
um mantém uma referência àCar
sua aplicada.Car
mantém apenas seu estado (para evitar confusão: propriedades) e não controla a adição e remoção deState
s:Aqui está o gerente. Ele superou
Car
o trabalho de (observável) de gerenciar seusState
s (observadores).Algumas coisas a ter em mente:
update
método dos observadores .update
método atual paraupdateOnAdd
eupdateOnRemove
se estiverem interessados apenas em uma dessas alterações. Em seguida, os métodosaddState
eremoveState
serão atualizados de acordo. Juntamente com o ponto anterior, essa abordagem pode acabar como um mecanismo robusto, extensível e flexível.State
quando isso acontece, pois não é importante para a pergunta. No entanto, no caso desta resposta, há o seguinte ponto a considerar. ComoState
agora deve ser criado com seuCar
(sem construtor vazio exposto) antes de chamar o método do gerente, os métodosaddState
eremoveState
não precisam de umCar
e podem apenas ser lidos emstate.car
.fonte