0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Observer Patternのコードの読み方

Last updated at Posted at 2021-04-17

はじめに

大規模なソフトウェアを読んでいて、Observer Patternのコードを読むといつも迷子になってしまう。

何かしらのイベントが発生すると、Subjectに事前に登録されているインスタンスのメソッドを呼び出す(本記事ではupdateメソッド)が、いつ、どこで、どんなインスタンスがSubjectに登録されたかは、Subjectのクラスだけを見てもわからない。

Observer Patternの全体の流れを理解して、
Subjectを生成している初期化処理が別の場所にあるので、そこを見なければいけない(本記事ではmain関数)。

Observer Pattern

知らせる側(Subject)が通知先(Observer,Listener)を事前に登録しておいて、
Subjectに更新があれば、Observerのメソッドを呼び出すことで、変更を通知する仕組み。

image.png

コードを実行する

Subject/Observerを生成

Subject/Observerを生成して、SubjectにObserverを登録させる。
Subjectの変化に応じて、Observerのメソッドが暗黙的に呼び出されるようになる。

int main() {
    auto sub = make_shared<Subject>(10);
    auto obs = make_unique<Observer>();

    sub->subscribe(obs.get());

    cout << "current state:" << sub->getState() << endl;
    sub->setState(15);
    cout << "current state:" << sub->getState() << endl;
    sub->setState(20);
    cout << "current state:" << sub->getState() << endl;
    sub->setState(25);
    cout << "current state:" << sub->getState() << endl;
}

実行時の出力

current state:10
Observer was notified update:15
current state:15
Observer was notified update:20
current state:20
Observer was notified update:25
current state:25

Subjectの定義

Subject::subscribe(Object)がmainから呼び出さ、内部でObserverを保持する。
Subjectの内部状態が変化する毎に、保持したObserverのupdae()を呼び出す。

class Subject {
 public:
    Subject(int s) : m_state{s}, m_observers{} {}
    virtual ~Subject() = default;
    
    // Subject::subscribe()がmain()から呼ばれる
    void subscribe(Observer* x) {
        x->setObject(this);
        m_observers.push_back(x);
    }
    int getState() const {
        return m_state;
    }
    void setState(int s) {
        m_state=s;
        notify();
    }

 private:
    void notify() {
        for(auto& i: m_observers){
            i->update();
        }
    }

    int m_state;
    vector<Observer*> m_observers;
};

Observerの定義

ObserverがSubjectを持てるようにsetObject()を用意する。
Subjectの変化時にupdate()が呼び出される。

class Observer {
 public:
    Observer() = default;
    virtual ~Observer() = default;

    void setObject(Subject* s) {
        m_subject.reset(s);
    }
    void update();

 private:
    unique_ptr<Subject> m_subject;
}

// CallBack
void Observer::update(){
    cout << "Observer was notified update:" << m_subject->getState() << endl;
}

コード全文

コードを実行する

# include<iostream>
# include<vector>
# include<memory>
using namespace std;

class Subject;

class Observer {
 public:
    Observer() = default;
    virtual ~Observer() = default;

    void setObject(Subject* s){
        m_subject.reset(s);
    }
    void update();

 private:
    unique_ptr<Subject> m_subject;
};

class Subject{
 public:
    Subject(int s) : m_state{s}, m_observers{} {}
    virtual ~Subject() = default;
    
    // Subject::subscribe()がmain()から呼ばれる
    void subscribe(Observer* x){
        x->setObject(this);
        m_observers.push_back(x);
    }
    int getState(){
        return m_state;
    }
    void setState(int s){
        m_state=s;
        notify();
    }

 private:
    void notify(){
        for(auto& i: m_observers){
            i->update();
        }
    }

    int m_state;
    vector<Observer*> m_observers;
};

void Observer::update(){
    cout << "Observer was notified update:" << m_subject->getState() << endl;
}


int main() {
    auto sub = make_shared<Subject>(10);
    auto obs = make_unique<Observer>();

    sub->subscribe(obs.get());

    cout << "current state:" << sub->getState() << endl;
    sub->setState(15);
    cout << "current state:" << sub->getState() << endl;
    sub->setState(20);
    cout << "current state:" << sub->getState() << endl;
    sub->setState(25);
    cout << "current state:" << sub->getState() << endl;
}

参考サイト

ゲームプログラマのためのデザインパターン(オブザーバ)

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?