Il modello di osservatore è adatto quando gli osservatori non sono indipendenti l'uno dall'altro?

9

Ho un class Car che ha 2 proprietà: int price e boolean inStock . Detiene anche un List di abstract class State (classe vuota). Ci sono 2 stati che possono essere applicati sull'auto e ognuno è rappresentato dalla sua stessa classe: class Upgrade extends State e class Shipping extends State .

Un Car può contenere qualsiasi numero di ciascuno dei 2 stati. Gli stati hanno le seguenti regole:

  • Upgrade : aggiunge 1 al prezzo per ogni stato applicato all'automobile dopo di sé.
  • Shipping : se c'è almeno l'1% di stato diShipping nell'elenco, quindi inStock è impostato su false .

Ad esempio, iniziando con 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

Stavo pensando al modello di osservatore in cui ogni operazione di aggiunta e rimozione notifica agli osservatori. Avevo in mente qualcosa di simile, ma non obbedisce alle regole che ho posto:

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);
    }
}

Ovviamente, questo non funziona. Quando viene rimosso un Shipping , qualcosa deve controllare se c'è un altro stato che imposta inStock su falso, quindi una rimozione di Shipping non può essere solo inStock = true . Upgrade aumenta price ad ogni chiamata. Ho quindi aggiunto costanti per i valori predefiniti e ho tentato un ricalcolo basato su quelli.

Non sto affatto cercando di imporre alcun modello, sto solo cercando di trovare una soluzione per i requisiti di cui sopra. Nota che in pratica Car contiene molte proprietà e ci sono molti stati che possono essere applicati in questo modo. Ho pensato ad alcuni modi per farlo:

  1. Poiché ogni osservatore riceve Car , può guardare tutti gli altri osservatori attualmente registrati e apportare una modifica in base a ciò. Non so se è intelligente intrigare osservatori come questo.
  2. Quando un osservatore viene aggiunto o rimosso in Car , ci sarà un ricalcolo. Tuttavia, questo ricalcolo dovrà essere eseguito su tutti osservatori indipendentemente da quello appena aggiunto / rimosso.
  3. Avere una classe "manager" esterna che chiamerà i metodi add e remove e ricalcola.

Che cos'è un buon schema di progettazione per implementare il comportamento descritto e come funzionerebbe?

    
posta user1803551 26.03.2016 - 20:03
fonte

2 risposte

0

Ho finito per andare con l'opzione 3 - utilizzando un gestore esterno. Il gestore è responsabile dell'aggiunta e rimozione di State s da Car s e della notifica agli osservatori quando queste modifiche si verificano.

Ecco come ho modificato il codice. Ho rimosso Observable / Observer di JDK perché sto facendo la mia implementazione.

Ogni State mantiene un riferimento al Car a cui è stato applicato.

abstract class State {

    Car car;

    State(Card car) { this.car = car; }

    public abstract void update();
}

class Upgrade extends State {

    @Override
    public void update() {

        int bonus = car.states.size() - car.states.indexOf(this) - 1;
        car.price += bonus;
        System.out.println(car.inStock + " " + car.price);
    }
}

class Shipping extends State {

    @Override
    public void update() {

        car.inStock = false;
        System.out.println(car.inStock + " " + car.price);
    }
}

Car mantiene solo il suo stato (per evitare confusione: proprietà) e non gestisce l'aggiunta e la rimozione di State s:

class Car extends Observable {

    List<State> states = new ArrayList<>();
    int price = 100;
    boolean inStock = true;
}

Ecco il manager. Ha superato il Car s (il lavoro osservabile) di gestirne il State s (osservatori).

class StatesManager {

    public void addState(Card car, State state) {

        car.states.add(state);
        for (State state : car. states)
            state.update;
    }

    public void removeState(Card car, State state) {

        car.states.remove(state);
        for (State state : car. states)
            state.update;
    }
}

Alcune cose da tenere a mente:

  • Tutti gli osservatori vengono avvisati di ogni cambiamento. Uno schema di distribuzione degli eventi più intelligente può eliminare le chiamate non necessarie al metodo update degli osservatori.
  • Gli osservatori potrebbero voler esporre più metodi di "aggiornamento" per le diverse occasioni. Ad esempio, possono dividere il metodo attuale update in updateOnAdd e updateOnRemove se sono interessati solo a una di queste modifiche. Quindi i metodi addState e removeState verrebbero aggiornati di conseguenza. Insieme al punto precedente questo approccio può diventare un meccanismo robusto, estensibile e flessibile.
  • Non ho specificato cosa fornisce l'istruzione per aggiungere e rimuovere State s e quando ciò accade non è importante per la domanda. Tuttavia, nel caso di questa risposta, c'è il seguente punto da considerare. Poiché State ora deve essere creato con il suo Car (nessun costruttore vuoto esposto) prima di chiamare il metodo del gestore, i metodi addState e removeState non devono prendere un Car e possono semplicemente leggerlo da state.car .
  • Gli osservatori vengono avvisati in ordine di registrazione sull'osservabile di default. È possibile specificare un altro ordine.
risposta data 01.04.2017 - 14:56
fonte
1

Gli osservatori lavorerebbero alla grande se si calcolasse il sistema in modo diverso. Invece di rendere gli stati essi stessi degli osservatori, potresti fare in modo che 2 nuove classi siano "osservatori del cambiamento di stato": un osservatore aggiorna il "prezzo", un altro aggiornerebbe "inStock". In questo modo sarebbero indipendenti se non si hanno regole per il prezzo a seconda di inStock o viceversa, cioè se tutto potrebbe essere calcolato guardando solo i cambiamenti di stato. Questa tecnica è chiamata "event sourcing" (ad esempio vedi - link ). È un modello in programmazione che ha alcune applicazioni degne di nota.

Rispondendo a una domanda più generale, a volte hai davvero delle dipendenze tra gli osservatori. Ad esempio, potresti volere che un osservatore reagisca prima di un altro. In tal caso, in genere è possibile eseguire un'implementazione personalizzata della classe Observable per gestire ordini o dipendenze.

    
risposta data 21.08.2016 - 02:16
fonte

Leggi altre domande sui tag