Wzorzec Obserwator

wpis w: Blog | 0

Ostatnim z wzorców opisywanych w tej serii będzie wzorzec obserwator. Standardowo przyjrzyjmy się pierw definicji tego wzorca.

Wzorzec obserwator definiuje pomiędzy obiektami zależność jeden-do-wielu w taki sposób, że kiedy wybrany obiekt zmienia swój stan, to wszystkie jego obiekty zależne zostają o tym powiadomione i automatycznie zaktualizowane.

Eric Freeman, Elisabeth Freeman, Bert Bates, Kathy Sierra – „Rusz głową! Wzorce Projektowe”

Wzorzec obserwator najlepiej zrozumieć na podstawie newslettera, działa on bardzo podobnie. Gdy ktoś zapisuje swój e-mail do jakiegoś newslettera dostaje jego nowe wydanie co miesiąc na swoją skrzynkę pocztową. Dzieję się to do tego momentu póki ktoś nie zrezygnuje z subskrypcji takiego newslettera. Na takiej samej zasadzie działa wzorzec obserwator, stąd ta nazwa zależności jeden-do-wielu, czyli jest jeden obiekt obserwowany (inaczej zwany Podmiot) w domyśle można powiedzieć, że jest to newsletter oraz wielu obiektów obserwujących, czyli każdy użytkownik newslettera z aktywną subskrypcją. Przyjrzyjmy się diagramowi klas wzorca obserwator.

Diagram klas wzorca obserwator (źródło: Eric Freeman, Elisabeth Freeman, Bert Bates, Kathy Sierra – „Rusz głową! Wzorce Projektowe” ) .

Jak widzimy mamy zdefiniowane dwa interfejsy: Podmiot oraz Obserwator. Interfejsy te implementują odpowiednie klasy obserwowane (Podmiot) i obserwujące. Widzimy, że Podmiot posiada takie metody jak rejestracja obserwatora, usuniecie obserwatora oraz powiadomienie obserwatorów, a obserwator ma metodę aktualizacji. Poniżej stworzyłem przykładowy kod aplikacji opartej na wzorcu Obserwator, aby lepiej zobrazować jego działanie. Jest to aplikacja śledząca wyniki meczów na żywo.

  1. W pierwszym kroku tworzymy interfejs Podmiotu oraz Obserwatora.
public interface Podmiot {
    public void dodajObserwatora(Obserwator obserwator);
    public void usunObserwatora(Obserwator obserwator);
    public void powiadomObserwatorow();
}

public interface Obserwator {
    public void aktualizacja(String mecz1,String mecz2, String mecz3);
}

2. Kolejnym etapem to stworzenie klasy Podmiotu oraz Obserwatora

public class BazaMeczy implements Podmiot{
    private ArrayList obserwatorzy;
    private String mecz1;
    private String mecz2;
    private String mecz3;

    public BazaMeczy(){
        obserwatorzy = new ArrayList();
    }


    @Override
    public void dodajObserwatora(Obserwator obserwator) {
        obserwatorzy.add((obserwator));
    }

    @Override
    public void usunObserwatora(Obserwator obserwator) {
        int i = obserwatorzy.indexOf(obserwator);
        if(i >= 0){
            obserwatorzy.remove(i);
        }
    }

    @Override
    public void powiadomObserwatorow() {
        for(int i = 0; i < obserwatorzy.size(); i++){
            Obserwator obserwator = (Obserwator) obserwatorzy.get(i);
            obserwator.aktualizacja(mecz1, mecz2, mecz3);
        }
    }

    public void zmianaWynikow(String mecz1, String mecz2, String mecz3){
        this.mecz1 = mecz1;
        this.mecz2 = mecz2;
        this.mecz3 = mecz3;
        System.out.println("Nastapila zmiana wynikow meczy...");
        powiadomObserwatorow();
    }
}

public class AplikacjaMeczowa implements Obserwator{
    private Podmiot bazaMeczy;
    private String mecz1;
    private String mecz2;
    private String mecz3;

    public AplikacjaMeczowa(Podmiot bazaMeczy){
        this.bazaMeczy = bazaMeczy;
        bazaMeczy.dodajObserwatora(this);
    }

    @Override
    public void aktualizacja(String mecz1, String mecz2, String mecz3) {
        this.mecz1 = mecz1;
        this.mecz2 = mecz2;
        this.mecz3 = mecz3;
        wyswietlWyniki();
    }

    public void wyswietlWyniki(){
        System.out.println(">>> Mecz Pierwszy <<<");
        System.out.println(mecz1);
        System.out.println(">>> Mecz Drugi <<<");
        System.out.println(mecz2);
        System.out.println(">>> Mecz Trzeci <<<");
        System.out.println(mecz3);
    }
}

3. Ostatni etap to przetestowanie działania aplikacji.

public class Test {
    public static void main (String[] args){
        BazaMeczy bazaMeczy = new BazaMeczy();
        AplikacjaMeczowa aplikacjaMeczowa = new AplikacjaMeczowa(bazaMeczy);

        bazaMeczy.zmianaWynikow("Polska 0:0 Niemcy","Anglia 0:0 Wlochy", "Holandia 0:0 Hiszpania");
        bazaMeczy.zmianaWynikow("Polska 1:0 Niemcy","Anglia 0:0 Wlochy", "Holandia 1:0 Hiszpania");
        bazaMeczy.zmianaWynikow("Polska 2:0 Niemcy","Anglia 0:1 Wlochy", "Holandia 1:0 Hiszpania");
    }
}

Podsumowując wzorzec Obserwator:

  • wzorzec definiuje relacje jeden-do-wielu
  • obiekty obserwowane (Podmiot) przesyłają powiadomienia i aktualizacje do obiektów obserwujących przy użyciu jednego interfejsu
  • obiekty obserwujące są luźno powiązane z obiektem obserwowanym
  • minusem jest to, że Obserwator powiadomiony o zmianie sam musi dojść do tego co się zmieniło w obiekcie obserwowanym, a zzasami takie sprawdzenie może nie być trywialne

Źródła:

  • Eric Freeman, Elisabeth Freeman, Bert Bates, Kathy Sierra – „Rusz głową! Wzorce Projektowe”