Tvorba aplikací pro Android

07

Návrhový vzor Pozorovatel
(Observer)

Na tomto místě zmíníme jedno téma, které v podstatě s ap­likacemi pro An­droid nesouvisí. Pro pokračování v příkladu z minulé kapito­ly totiž pot­řebujeme předat in­for­mace o případných změnách záznamů či je­jich přidání nebo odebrání kom­ponentě ListView. Pro tyto situace se dobře hodí návrhový vzor Pozorovatel/Pos­luchač (Ob­server/Lis­ten­er). Nebudeme zde pro­blematiku návrhových vzorů rozebírat pod­robně, ale zmíníme al­es­poň základní fakta. Om­louváme se pokročilejším čtenářům za zjed­noduš­ení, kterých se tím v některých as­pek­tech dopus­tíme. Věříme, že pro naši cílovou skupinu čtenářů jsou tato zjed­noduš­ení vhodná.


Co jsou návrhové vzory?

Pro­gramátoři s delší praxí brzy zjistí, že některé situace se v pro­jek­tech op­akují. V našem případě je to situace, kdy jedna třída potřebuje být in­for­mována o změnách jiné třídy. Ať už se jedná o okno, které má rea­govat na stisk tlačítka, o tabul­ku, která se má přek­reslit v důs­ledku načtení nových dat, nebo o ListView, kde pot­řebujeme ak­tualizovat data po úpravě některého záznamu.


Pro podobné situace a společné šab­lony řešení těchto situací se používá označení návrhové vzory (de­sign pat­terns). Ex­is­tuje tedy například návrhový vzor Pozorovatel/Pos­luchač (Ob­server/Lis­ten­er), který popisuje obecné řešení situace, kdy jedna třída potřebuje být in­for­mována, kdykoli nas­tane zvol­ená událost v jiné třídě. Ex­is­tují ale také další návrhové vzory pro jiné, často op­akované situace.


Návrhový vzor Pozorovatel v Javě

Protože se návrhové vzory vys­kytují často, nabízí moderní pro­gramovací jazyky v rámci svých knihov­en stan­dardizované im­plemen­tace těchto návrhových vzorů. Pro návrhový vzor Pozorovatel nabízí Java třídu Observable a rozhraní Observer, které nám zjed­noduší použití návrhového vzoru


Třída Observable

Třída Observable im­plemen­tuje chování ob­jektu, ve kterém se dějí změny a který chce o těchto změnách in­for­movat pozorovatele, kteří o to požádají. Nabízí metody:

  • addObserver(Observer o)
    Přidá do sez­namu pozorovatelů nový ob­jekt. Kdykoli nas­tane změna, budou všichni takto zařazení pozorovatelé in­for­mování o této změně.

  • setChanged()
    Tuto metodu voláme v okamžiku, kdy nas­tane událost, o které mají být pozorovatelé in­for­mování. Metodu lze volat op­akovaně.

  • notifyObservers()
    Tato metoda zkontroluje, zda došlo ke změně (zda byla volána metoda setChan­ged()). Pokud ano, rozešle všem pozorovatelům in­for­maci o tom, že nas­ta­ly změny.

Třída nabízí i další metody, ale ty nebudeme v našem pro­jek­tu pot­řebovat.


Rozhraní Observer

Rozhraní Ob­serv­er im­plemen­tují ty třídy, které chtějí být in­for­movány o změnách. Im­plemen­tace rozhraní vyžaduje přítom­nost jediné metody:

  • update(Observable zdroj, Object o)
    Tuto metodu zavolá potomek třídy Observable v okamžiku, kdy bude chtít pozorovatele in­for­movat o nast­alé změně.

Pokračování příkladu – Můj pestrý život

Využijeme návrhového vzoru Pozorovatel k předávání in­for­mací o změnách v záz­namech. Třída Zaznam bude Observable a bude sdělovat in­for­mace o změně svého stavu třídě Data. Třída data bude také Observable a předá in­for­mace o přidání, změně či rušení položek v sez­namu záznamů hlavní ak­tivitě a tedy i kom­ponentě ListView.


  1. Třídy ZaznamData nas­tavíme jako potoky třídy Observable:

  2. public class Zaznam extends Observable {
    
    public class Data extends Observable implements Observer{
     
    

  3. Ve třídě Data im­plemen­tujeme rozhraní Observer a přidáme metodu update():

  4. public class Data extends Observable implements Observer{
         
        @Override
        public void update(Observable observable, Object o) {
            this.setChanged();
            this.notifyObservers();
        }
    

  5. Také ve hlavní ak­tivitě je třeba im­plemen­tovat rozhraní Ob­serv­er a překrýt metodu up­date(). Z metody up­date() zavoláme metodu ak­tualizuj(), kterou jsme si nac­hystali už v předchozí kapitole:

  6. public class MainActivity extends AppCompatActivity implements Observer {
        ...
        @Override
        public void update(Observable observable, Object o) {
            this.aktualizuj();
    }
    

  7. Při jakékoli změně dat, která se má oznámit pozorovatelům, doplníme volání metod setChanged()notifyObservers(). Při každém přidávání záznamu ve třídě Data navíc musíme přidat novému záznamu jako pozorovatele aktuální in­stan­ci třídy Data:

  8. public class Zaznam extends Observable {
        
        public void invertujDulezite() {
            this.dulezite = ! this.dulezite;
            this.setChanged();
            this.notifyObservers();
        }
    
    public class Data extends Observable implements Observer{
        ArrayList<Zaznam> data = new ArrayList<Zaznam>();
        protected void nactiData() {
            ...
            
            z = new Zaznam("Terra Mystica", "Naučil jsem se novou deskovou hru.", false);
            z.addObserver(this);
            this.data.add(z);
            this.setChanged();
            this.notifyObservers();
        }
        ...
        @Override
        public void update(Observable observable, Object o) {
            this.setChanged();
            this.notifyObservers();
        }
    

Shrnutí

  1. Pokud chce třída předávat os­tatním in­for­mace o změně dat, může zdědit od třídy Observable metody:

    1. addObserver(Observer o)… přidá do sez­namu pozorovatelů další třídu pozorovatel,
    2. setChanged()… volá se v okamžiku, kdy se změní at­ributy třídy,
    3. a notifyObservers()… volá se v okamžiku, kdy chceme in­for­movat pozorovatele.

  2. Pozorovatel musí im­plemen­tovat rozhraní Observable a jeho metodu update(Observable zdroj, Object zprava).

  3. Ob­dobný mech­aniz­mus pos­luchačů (Listener) používá i knihov­na Swing pro tvor­bu grafic­kého rozhraní ap­likací a další knihov­ny Javy.