Tengo un class Car
que tiene 2 propiedades: int price
y boolean inStock
. También contiene un List
de abstract class State
(clase vacía). Hay 2 estados que se pueden aplicar en el automóvil y cada uno está representado por su propia clase: class Upgrade extends State
y class Shipping extends State
.
A Car
puede contener cualquier número de cada uno de los 2 estados. Los estados tienen las siguientes reglas:
Upgrade
: se suma1
al precio de cada estado aplicado al automóvil después de sí mismo.Shipping
: si hay al menos 1Shipping
estado en la lista,inStock
se establece enfalse
.
Por ejemplo, comenzando con price = 1
y 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
Estaba pensando en el patrón de observador donde cada operación de agregar y quitar notifica a los observadores. Tenía algo como esto en mente, pero no obedece las reglas que planteé:
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, esto no funciona. Cuando Shipping
se elimina a, algo tiene que verificar si hay otra configuración de estado inStock
en falso, por lo que la eliminación de Shipping
no puede simplemente inStock = true
. Upgrade
aumenta price
en cada llamada. Luego agregué constantes para los valores predeterminados e intenté un recálculo basado en esos.
De ninguna manera estoy tratando de imponer ningún patrón, solo estoy tratando de encontrar una solución para los requisitos anteriores. Tenga en cuenta que en la práctica Car
contiene muchas propiedades y hay muchos estados que se pueden aplicar de esta manera. Pensé en algunas maneras de hacer esto:
- Como cada observador recibe
Car
, puede observar a todos los otros observadores actualmente registrados y hacer un cambio basado en eso. No sé si es inteligente enredar a observadores como este. - Cuando se agrega o elimina un observador
Car
, habrá un nuevo cálculo. Sin embargo, este recálculo deberá realizarse en todos los observadores, independientemente del que se acaba de agregar / eliminar. - Tenga una clase de "administrador" externa que llamará a los métodos de agregar y quitar y hará el recálculo.
¿Cuál es un buen patrón de diseño para implementar el comportamiento descrito y cómo funcionaría?
fuente
Respuestas:
Los observadores funcionarían muy bien si factorizaras el sistema de manera diferente. En lugar de hacer que los estados sean observadores, puede hacer que 2 nuevas clases sean "observadores de cambio de estado": un observador actualizará "precio", otro actualizará "inStock". De esta manera, serían independientes si no tiene reglas de precio que dependan de inStock o viceversa, es decir, si todo pudiera calcularse simplemente mirando los cambios de estado. Esta técnica se denomina "abastecimiento de eventos" (por ejemplo, consulte - https://ookami86.github.io/event-sourcing-in-practice/ ). Es un patrón en la programación que tiene algunas aplicaciones notables.
Respondiendo a una pregunta más general, a veces realmente tienes dependencias entre los observadores. Por ejemplo, es posible que desee que un observador reaccione antes que otro. En tal caso, generalmente es posible hacer una implementación personalizada de la clase Observable para tratar con pedidos o dependencias.
fuente
Terminé con la opción 3: usar un administrador externo. El gerente es responsable de agregar y eliminar
State
s deCar
s y de notificar a los observadores cuando ocurren estos cambios.Así es como modifiqué el código. Eliminé el
Observable
/Observer
del JDK porque estoy haciendo mi propia implementación.Cada uno
State
mantiene una referencia aCar
su aplicada.Car
solo mantiene su estado (para evitar confusiones: propiedades) y no maneja agregar y eliminarState
s:Aquí está el gerente. Ha superado
Car
el trabajo de s (el observable) de administrar susState
s (observadores).Un par de cosas a tener en cuenta:
update
método de los observadores .update
método actual enupdateOnAdd
yupdateOnRemove
si están interesados solo en uno de estos cambios. Luego, los métodosaddState
yremoveState
se actualizarían en consecuencia. Junto con el punto anterior, este enfoque puede terminar como un mecanismo robusto, extensible y flexible.State
sy cuándo sucede eso, ya que no es importante para la pregunta. Sin embargo, en el caso de esta respuesta, hay que considerar el siguiente punto. ComoState
ahora debe crearse con suCar
(sin un constructor vacío expuesto) antes de llamar al método del administrador, los métodosaddState
yremoveState
no necesitan tomar unCar
y solo pueden leerlostate.car
.fuente