Wzorzec Projektowy Obserwator

Czyli jak obserwować w programowaniu

Obserwator określa zależność pomiędzy jedną klasą a wieloma. W praktyce wygląda to tak, że jeden obiekt kiedy się zaktualizuje wysyła powiadomienie do obiektów, które go obserwują że się zmienił i one mogą np. zaktualizować dane.

A tak po programistycznemu

Tworzymy klasę abstrakcyjną, która będzie ‘podmiotem’ – ona będzie wysyłać powiadomienia że się zmieniła. Będzie również zawierała listę klas które ma powiadomić. Przykładowo:

 class Subject {
public:
virtual void attach(Observer*);
virtual void detach(Observer*);
virtual void notify();
private:
List<Observer*> observers;
}

void Subject::attach(Observer* o) {
observers.append(o);
}

void Subject::detach(Observer* o) {
observers.remove(o);
}

void Subject::notify() {
for(o in observers) {
o->update(this);
}
} 

I teraz czas na naszych obserwatorów, którzy będą czekać na zmianę. Tworzymy klasę abstrakcyjną która może wyglądać tak:


class Observer {
public:
virtual void update(Subject* changedSubject);
}

I teraz kiedy mamy oba interfejsy zdefiniowane możemy zacząć to wszystko łączyć.

Przykład

Posłużę się przykładem, który być może zaimplementuję w swoim projekcie. Kiedy gracz wyrzuca kartę, odpala się animacja. Może nie jest to najlepszy przykład, ale nic innego na razie nie przychodzi mi do głowy 🙂 Tak więc:


class Player : public Subject {
public:
Card currentCard;
...

void useCard();
}

void Player::useCard() {
//aktualizacja rzucania karty, gry itd.
notify(); //powiadom obserwatorów że została użyta karta
}

Mamy gracza, który kiedy użyje jakiejś karty wszyscy obserwatorzy zostaną powiadomieni. Zaimplementujmy obserwatora:


class Animation : public Observer {
public:
Animation(Player*);
~Animation();

void update(Subject*); // z klasy Observer
void draw(); // do rysowania
private:
Player* player;
}

Animation::Animation(Player* player) {
this->player = player; // bierzemy podmiot do obserwowania
this->player->attach(this); // mówimy mu że go obserwujemy
}

Animation::~Animation() {
this->player.detach(this); // przestajemy obserwować
}

void Animation::update(Subject* changedSubject) {
if(changedSubject == player) { // jeśli zmieniło się coś w podmiocie (graczu)
draw();
}
}

void Anmiation::draw() {
Card cardToDraw = player->currentCard; // bierzemy kartę od gracza
// wyświetlamy animację karty
}

Cudowne. A teraz łączymy wszystko razem:

Player* player = new Player();
Animation* animation = new Animation(player);

Za każdym razem jak gracz użyje jakiejś karty zostanie o tym powiadomiony animation i narysuje on odpowiednią animację w zależności od obecnej karty.

Co dalej?

Postaram się rozwinąć ten temat i inne wzorce projektowe w kolejnych postach oraz jak ich używam w tworzeniu swojej gry.

Please follow and like us:
0