5

Mi rendo conto che questo argomento è stato coperto a morte, ma sono ancora in difficoltà e potrebbe fare con qualche aiuto specifico.Schema di osservatore ad accoppiamento lento

Il mio obiettivo è quello di implementare un semplice modello di Osservatore tra un tipo di osservabile (diciamo un cane) e un qualche tipo di ascoltatore (diciamo Proprietario).

Eventualmente il proprietario sarebbe una "vista" e il cane un "modello" in un paradigma MVC. Sto usando Dog and Owner solo per cercare di semplificare le cose qui.

Ho tentato di utilizzare le classi Observer/Observable di Java, ma ho capito quanto sia pessimo il metodo update() di Observers: riceve un POJO e dovrei controllare/cast quel POJO nel metodo update(). Preferirei che il mio metodo 'update()' ottenga qualcosa che può aspettarsi.

Così, ho seguito un paio di tutorial, compreso questo, che utilizza il cane/proprietario a titolo di esempio:

http://www.youtube.com/watch?v=qw0zZAte66A

Qui mi è stato mostrato come rotolare il mio Observer/classi osservati. In pseudo-codice, quello che ora ho è questa:

Dog/Model { 

    List listeners; 

    public fireDogHungryEvent() { 

     foreach listener { 
      listener.onDogHungry(this); 
     } 
    } 

    public fireDogPeeEvent() { 

     foreach listener { 
      listener.onDogNeedsToPee(this); 
     } 
    } 

    public getHungerLevel() { return hungerLevel; } 
    public getBladderCapacity() { return bladderCapacity; } 
} 

Owner/View { 

    public onDogHungry(model) { 
     println(model.getHungerLevel()); 
    } 

    public onDogNeedsToPee(model) { 
     println(model.getBladderCapacity()); 
    } 
} 

Così ora metodo piuttosto che un aggiornamento(), ho metodi che gestiscono eventi specifici. Brillante. Al momento sono soddisfatto della classe Owner/view. Conosce i metodi del cane/modello e va bene (credo).

Quello che non mi piace è che il cane/modello ha riferimenti ai metodi nel proprietario/vista. Ho letto innumerevoli volte e sono completamente d'accordo sul fatto che un modello non dovrebbe essere strettamente accoppiato alle sue opinioni, come sembra essere al di sopra. Anch'io non sono entusiasta di tutti i metodi di 'fuoco' nel Cane/modello che fanno quasi la stessa cosa; looping su tutti i suoi ascoltatori e solo chiamando un metodo diverso su ciascun ascoltatore.

E 'possibile disaccoppiare ulteriormente questa relazione e non avere i metodi specifici di chiamata Cane/modello? In tal caso, qual è il modo migliore per andare a ricevere i dati Cane/Modello nel Proprietario/vista e lavorare in modo appropriato?

Grazie

+0

Quali metodi di 'Cane' hanno riferimenti alla vista? Io non ne vedo. – SJuan76

+0

Il modo migliore è utilizzare un'interfaccia, suggerita dal tutorial video. L'ascoltatore è un'interfaccia nella tua implementazione? –

+0

SJuan76 - i riferimenti di cui sto parlando sono le chiamate a listener.onDogHungry e listener.onDogNeedsToPee nei metodi Dog 'fire'. – whoshotdk

risposta

3

Si dovrebbe interface via la conoscenza della specifica implementazione sia dal Observer e Observable

public enum EventType { 

    HUNGRY, 
    PEE; 
} 

public interface DogEvent { 

    EventType getType(); 
} 

public interface DogListener { 

    void fireEvent(DogEvent event); 
} 

public class Dog { 

    private final Set<DogListener> listeners = new CopyOnWriteArraySet<DogListener>(); 

    public void register(final DogListener dogListener) { 
     listeners.add(dogListener); 
    } 

    public void unregister(final DogListener dogListener) { 
     listeners.remove(dogListener); 
    } 

    public void firePeeEvent() { 
     fireEvent(new DogEvent() { 
      @Override 
      public EventType getType() { 
       return EventType.PEE; 
      } 
     }); 
    } 

    public void fireHungryEvent() { 
     fireEvent(new DogEvent() { 
      @Override 
      public EventType getType() { 
       return EventType.HUNGRY; 
      } 
     }); 
    } 

    private void fireEvent(final DogEvent dogEvent) { 
     for (final DogListener listener : listeners) { 
      listener.fireEvent(dogEvent); 
     } 
    } 
} 

public class Owner implements DogListener { 

    @Override 
    public void fireEvent(DogEvent event) { 
     switch (event.getType()) { 
      case PEE: 
       System.out.println("Someone take the dog out"); 
       break; 
      case HUNGRY: 
       System.out.println("I can't believe the dog is hungry _again_!"); 
       break; 
     } 
    } 
} 

In questo caso il Dog non sa circa l'attuazione del Owner solo noto che la Owner è un DogListener.

Il Owner d'altra parte non è al corrente dello Dog, ma sa solo che ha ricevuto DogEvent.

+0

Mi piace questa risposta per la sua chiarezza, grazie Boris. Non mi piace * particolarmente * come la dichiarazione del caso che deve verificare quale tipo di evento è accaduto, ma immagino che non succeda semplicemente per magia: D Darò questo risultato con un paio di osservabili/ascoltatori e vedremo come andrò avanti. Ad un certo punto, accetterò questa risposta se riuscirò a trovare qualcosa di abbastanza efficiente con esso! – whoshotdk

+0

Ah, ho appena realizzato che si tratta di un'implementazione molto migliore di ciò che ho provato in precedenza nei miei tentativi; eccetto che stavo usando reflection per invocare metodi basati sul nome della stringa invece di un oggetto evento. Penso che funzionerà: D – whoshotdk

+0

L'enum era solo un suggerimento: si poteva usare un modello di visitatore per sfruttare appieno il polimorfismo. –

1

Ai fini MVC (soprattutto per MVP) ho raccolto esperienze positive con la programmazione basi eventi. Quindi avresti bisogno di un EventBus che sarà usato da Dog e Owner. Il proprietario si iscriverà per alcune classi di eventi come HungerLevelIncrease e il cane licenzierà tali eventi. Per il tuo proprietario di un cane questo sarebbe un po 'strano perché il proprietario non conosceva i suoi cani, ma per la programmazione della GUI questa è una soluzione semplice e facile per disaccoppiare le cose, specialmente tra gli altri controller.

È possibile creare facilmente un autobus da soli oppure è possibile utilizzare quello da google guava.

Questo è il modo migliore che conosco per creare un accoppiamento molto morbido.

+0

Inizialmente mi è sembrata una buona idea, ma dopo un po 'di lettura, altri hanno notato che questo schema rende essenzialmente necessario conoscere ogni ascoltatore/ascoltatore su un bus. Aggiungere nuovi tipi di eventi significherebbe cambiare anche quel bus. Vedi: http://stackoverflow.com/questions/3987391/why-people-use-message-event-buses-in-their-code. Forse non è così male come sembra, idk. – whoshotdk

+1

@ user2373021 hai guardato qualcosa come [mbassador] (https://github.com/bennidi/mbassador) - è un'annotazione basata così tutto ciò che serve è annotare i tuoi ascoltatori e dedurrà ciò che vogliono ascoltare dal parametri del metodo. Può anche inviare messaggi in modo asincrono ... –

+0

@ user2373021 è vero che è necessario aggiungere il bus a tutte le classi che hanno bisogno di lui. Tuttavia, la maggior parte delle volte si aggiungerà comunque un contenitore per le dipendenze alle applicazioni più grandi che possono iniettarvi l'EventBus per voi. Ma l'aggiunta di nuovi tipi di eventi non dovrebbe influenzare il tuo bus. Se hai solo poche lezioni, vorrei andare anche per la semplice soluzione listener, ma se ottieni più dialoghi e più dialoghi si parlano tra loro e con altre classi (ad es. Server). Vorrei provare il meccanismo EventBus. – mszalbach

Problemi correlati