Modelli di design per Android The Observer Pattern

Qual è il modello di osservatore?

The Observer Pattern è un pattern di progettazione software che stabilisce una dipendenza uno-a-molti tra gli oggetti. Ogni volta che lo stato di uno degli oggetti (il "soggetto" o "osservabile") cambia, vengono notificati tutti gli altri oggetti ("osservatori") che dipendono da esso.

Usiamo l'esempio degli utenti che si sono abbonati per ricevere offerte da Envato Market via email. Gli utenti in questo caso sono osservatori. Ogni volta che c'è un'offerta da parte di Envato Market, ricevono una notifica al riguardo via e-mail. Ogni utente può quindi acquistare nell'offerta o decidere che potrebbe non essere realmente interessato a esso in quel momento. Un utente (un osservatore) può anche iscriversi per ricevere offerte da un altro mercato e-commerce, se lo desidera e in seguito potrebbe annullare completamente l'iscrizione a ricevere offerte da qualcuno di loro. 

Questo modello è molto simile al modello Publish-Subscribe. Il soggetto o osservabile pubblica una notifica agli osservatori dipendenti senza nemmeno sapere quanti osservatori l'hanno sottoscritto o chi sono: l'osservabile sa solo che dovrebbero implementare un'interfaccia (ci arriveremo a breve), senza preoccuparci su quale azione potrebbero svolgere gli osservatori.

Benefici del modello di osservatore

  • Il soggetto conosce poco dei suoi osservatori. L'unica cosa che sa è che gli osservatori implementano o accettano un determinato contratto o interfaccia. 
  • I soggetti possono essere riutilizzati senza coinvolgere i loro osservatori, e lo stesso vale per gli osservatori.
  • Nessuna modifica viene apportata al soggetto per accogliere un nuovo osservatore. Il nuovo osservatore deve solo implementare un'interfaccia di cui il soggetto è a conoscenza e quindi registrarsi al soggetto.  
  • Un osservatore può essere registrato su più di un argomento per il quale è registrato.

Tutti questi vantaggi ti offrono un accoppiamento libero tra i moduli del tuo codice, che ti consente di creare un design flessibile per la tua applicazione. Nel resto di questo post, vedremo come creare la nostra implementazione del pattern Observer, e useremo anche l'API Java Observer / Observable integrata oltre a cercare nelle librerie di terze parti in grado di offrire tale funzionalità. 

Costruire il nostro modello di osservatore

1. Creare l'interfaccia soggetto

Iniziamo definendo un'interfaccia che gli oggetti (osservabili) implementeranno.

interfaccia pubblica Oggetto void registerObserver (RepositoryObserver repositoryObserver); void removeObserver (RepositoryObserver repositoryObserver); void notifyObservers (); 

Nel codice sopra, abbiamo creato un'interfaccia Java con tre metodi. Il primo metodo registerObserver (), come dice, registrerà un osservatore di tipo RepositoryObserver (creeremo l'interfaccia a breve) sull'argomento. removeObserver () sarà chiamato per rimuovere un osservatore che vuole smettere di ricevere notifiche dal soggetto, e infine, notifyObserver () invierà una trasmissione a tutti gli osservatori ogni volta che c'è un cambiamento. Ora, creiamo una classe oggetto concreta che implementerà l'interfaccia soggetto che abbiamo creato:

importa android.os.Handler; import java.util.ArrayList; La classe pubblica UserDataRepository implementa Subject private String mFullName; private int mAge; private static UserDataRepository INSTANCE = null; ArrayList privato mObservers; private UserDataRepository () mObservers = new ArrayList <> (); getNewDataFromRemote ();  // Simulate network private void getNewDataFromRemote () handler finale handler = new Handler (); handler.postDelayed (new Runnable () @Override public void run () setUserData ("Chike Mgbemena", 101);, 10000);  // Crea un Singleton della classe public static UserDataRepository getInstance () if (INSTANCE == null) INSTANCE = new UserDataRepository ();  return INSTANCE;  @Override public void registerObserver (RepositoryObserver repositoryObserver) if (! MObservers.contains (repositoryObserver)) mObservers.add (repositoryObserver);  @Override public void removeObserver (RepositoryObserver repositoryObserver) if (mObservers.contains (repositoryObserver)) mObservers.remove (repositoryObserver);  @Override public void notifyObservers () for (RepositoryObserver observer: mObservers) observer.onUserDataChanged (mFullName, mAge);  public void setUserData (String fullName, int age) mFullName = fullName; mAge = età; notifyObservers (); 

La classe sopra implementa il Soggetto interfaccia. Abbiamo un Lista di array che tiene gli osservatori e poi lo crea nel costruttore privato. Un osservatore si registra aggiungendo al Lista di array e allo stesso modo, cancella la registrazione rimuovendo dal  Lista di array

Si noti che stiamo simulando una richiesta di rete per recuperare i nuovi dati. Una volta il setUserData () il metodo viene chiamato e dato il nuovo valore per il nome completo e l'età, chiamiamo il notifyObservers () metodo che, come si dice, notifica o invia una trasmissione a tutti gli osservatori registrati sulla nuova modifica dei dati. Vengono anche passati i nuovi valori per il nome completo e l'età. Questo argomento può avere più osservatori ma, in questo tutorial, creeremo un solo osservatore. Ma prima, creiamo l'interfaccia dell'osservatore. 

2. Creare l'interfaccia Observer

interfaccia pubblica RepositoryObserver void onUserDataChanged (String fullname, int age); 

Nel codice sopra, abbiamo creato l'interfaccia dell'osservatore che gli osservatori concreti dovrebbero implementare. Ciò consente al nostro codice di essere più flessibile perché stiamo codificando su un'interfaccia invece che su un'implementazione concreta. Un concreto Soggetto la classe non ha bisogno di essere consapevole dei molti osservatori concreti che può avere; tutto ciò che sa su di loro è che implementano il RepositoryObserver interfaccia. 

Creiamo ora una classe concreta che implementa questa interfaccia.

importare android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; public class UserProfileActivity estende AppCompatActivity implementa RepositoryObserver private Subject mUserDataRepository; TextView privato mTextViewUserFullName; TextView privato mTextViewUserAge; @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_user_profile); mTextViewUserAge = (TextView) findViewById (R.id.tv_age); mTextViewUserFullName = (TextView) findViewById (R.id.tv_fullname); mUserDataRepository = UserDataRepository.getInstance (); mUserDataRepository.registerObserver (questo);  @Override public void onUserDataChanged (String fullname, int age) mTextViewUserFullName.setText (fullname); mTextViewUserAge.setText (età);  @Override protected void onDestroy () super.onDestroy (); mUserDataRepository.removeObserver (questo); 

La prima cosa da notare nel codice sopra è quella UserProfileActivity implementa il RepositoryObserver interfaccia, quindi deve implementare il metodo onUserDataChanged (). Nel onCreate () metodo dell'attività, abbiamo ottenuto un'istanza del UserDataRepository che abbiamo poi inizializzato e infine registrato questo osservatore a. 

Nel OnDestroy () metodo, vogliamo smettere di ricevere le notifiche, quindi annulliamo la registrazione da notifiche. 

Nel onUserDataChanged () metodo, vogliamo aggiornare il TextView widgets-mTextViewUserFullName e mTextViewUserAge-con il nuovo set di valori di dati.  

In questo momento abbiamo solo una classe di osservatori, ma è possibile e facile per noi creare altre classi che vogliono essere osservatori del UserDataRepository classe. Ad esempio, potremmo facilmente avere un SettingsActivity che vuole essere anche informato delle modifiche ai dati dell'utente diventando un osservatore. 

Spingere e tirare i modelli

Nell'esempio sopra, stiamo usando il modello push del pattern observer. In questo modello, il soggetto notifica agli osservatori il cambiamento passando i dati che sono cambiati. Ma nel modello pull, il soggetto informerà comunque gli osservatori, ma in realtà non passa i dati che sono cambiati. Gli osservatori quindi estraggono i dati di cui hanno bisogno una volta ricevuta la notifica. 

Utilizzo dell'API di Observer Built-in di Java

Finora, abbiamo creato la nostra implementazione del modello Observer, ma Java ha integrato il supporto Observer / Observable nella sua API. In questa sezione, useremo questo. Questa API semplifica alcune delle implementazioni, come vedrai. 

1. Creare l'osservabile

Nostro UserDataRepository-che è il nostro soggetto o osservabile - ora estenderà il java.util.Observable superclasse per diventare osservabile. Questa è una classe che vuole essere osservata da uno o più osservatori. 

importa android.os.Handler; import java.util.Observable; La classe pubblica UserDataRepository estende Observable private String mFullName; private int mAge; private static UserDataRepository INSTANCE = null; private UserDataRepository () getNewDataFromRemote ();  // Restituisce una singola istanza di questa classe, creandola se necessario. public static UserDataRepository getInstance () if (INSTANCE == null) INSTANCE = new UserDataRepository ();  return INSTANCE;  // Simulate network private void getNewDataFromRemote () handler finale handler = new Handler (); handler.postDelayed (new Runnable () @Override public void run () setUserData ("Mgbemena Chike", 102);, 10000);  public void setUserData (String fullName, int age) mFullName = fullName; mAge = età; setChanged (); notifyObservers ();  public String getFullName () return mFullName;  public int getAge () return mAge; 

Ora che abbiamo refactored il nostro UserDataRepository classe per utilizzare l'API Java Observable, vediamo cosa è cambiato rispetto alla versione precedente. La prima cosa da notare è che stiamo estendendo una super classe (questo significa che questa classe non può estendere nessuna altra classe) e non implementare un'interfaccia come abbiamo fatto nella sezione precedente. 

Non stiamo più tenendo un Lista di array di osservatori; questo è gestito nella super classe. Allo stesso modo, non dobbiamo preoccuparci di registrazione, rimozione o notifica degli osservatori-java.util.Observable sta gestendo tutti quelli per noi. 

Un'altra differenza è che in questa classe stiamo impiegando uno stile di attrazione. Allertiamo gli osservatori che è accaduto un cambiamento con notifyObservers (), ma gli osservatori dovranno estrarre i dati usando i getter di campo che abbiamo definito in questa classe. Se invece vuoi usare lo stile push, puoi usare il metodo notifyObservers (Object arg) e passare i dati modificati agli osservatori nell'argomento oggetto. 

Il setChanged () il metodo della super classe imposta un flag su true, indicando che i dati sono cambiati. Quindi puoi chiamare il notifyObservers () metodo. Tieni presente che se non chiami setChanged () prima di chiamare notifyObsevers (), gli osservatori non saranno avvisati. Puoi controllare il valore di questo flag usando il metodo è cambiato() e cancellarlo con false clearChanged (). Ora che abbiamo creato la nostra classe osservabile, vediamo come impostare anche un osservatore.  

2. Crea l'osservatore

Nostro UserDataRepository la classe osservabile ha bisogno di un osservatore corrispondente per essere utile, quindi ricontrolliamo il nostro UserProfileActivity implementare il java.util.Observer interfaccia. 

importare android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; import com.chikeandroid.tutsplusobserverpattern.R; import java.util.Observable; import java.util.Observer; public class UserProfileActivity estende AppCompatActivity implementa Observer private Observable mUserDataRepositoryObservable; TextView privato mTextViewUserFullName; TextView privato mTextViewUserAge; @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_user_profile); mTextViewUserAge = (TextView) findViewById (R.id.tv_age); mTextViewUserFullName = (TextView) findViewById (R.id.tv_fullname); mUserDataRepositoryObservable = UserDataRepository.getInstance (); mUserDataRepositoryObservable.addObserver (questo);  @Override aggiornamento pubblico vuoto (Osservabile osservabile, Oggetto o) if (istanza osservabile di UserDataRepository) UserDataRepository userDataRepository = (UserDataRepository) osservabile; mTextViewUserAge.setText (String.valueOf (userDataRepository.getAge ())); mTextViewUserFullName.setText (userDataRepository.getFullName ());  @Override protected void onDestroy () super.onDestroy (); mUserDataRepositoryObservable.deleteObserver (questo); 

Nel onCreate () metodo, aggiungiamo questa classe come osservatore al UserDataRepository osservabile usando il addObserver () metodo nel java.util.Observable super classe.  

Nel aggiornare() metodo che l'osservatore deve implementare, controlliamo se il Osservabile riceviamo come parametro un'istanza del nostro UserDataRepository (nota che un osservatore può iscriversi a diversi osservabili), e poi lo lanciamo su quell'istanza e recuperiamo i valori che vogliamo usando i getter di campo. Quindi usiamo questi valori per aggiornare i widget di visualizzazione. 

Quando l'attività viene distrutta, non è necessario ricevere aggiornamenti dall'osservabile, quindi rimuoveremo l'attività dall'elenco degli osservatori chiamando il metodo deleteObserver ()

Librerie per implementare un modello di osservatore

Se non si desidera creare da zero l'implementazione del proprio modello di osservatore o utilizzare l'API di Java Observer, è possibile utilizzare alcune librerie gratuite e open source disponibili per Android come GreenBot EventBus. Per saperne di più, guarda il mio tutorial qui su Envato Tuts+.

Oppure, ti potrebbe piacere RxAndroid e RxJava. Scopri di più su di loro qui:

Conclusione

In questo tutorial, hai imparato a conoscere il pattern Observer in Java: quali sono i vantaggi di utilizzarlo, come implementare il tuo, utilizzando l'API di Java Observer e anche alcune librerie di terze parti per l'implementazione di questo modello. 

Nel frattempo, dai uno sguardo ad alcuni dei nostri altri corsi e tutorial sul linguaggio Java e sullo sviluppo di app per Android!