Observer


で,何とか実装してみました.


#include <list>
#include <algorithm>
#include <assert.h>
#include <iostream>


class subject;

//
// Observerクラス
//
class observer {

protected:
  observer(){
  }

public:

  virtual ~observer(){}
  // 変更があった時(notify)subjectから呼ばれる
  virtual void subject_update(subject* sub) = 0;
  // observerが登録された時,登録したsubjectから呼ばれる
  virtual void subject_set(subject* sub) = 0;
  // observerが解除された時,解除したsubjectから呼ばれる
  virtual void subject_unset(subject* sub) = 0;
};

//
// Subjectクラス
//

class subject {

protected:
  std::list<observer*> observers_;
  subject(){}

public:

  virtual ~subject(){}

  virtual void attach(observer* obs){
    // 登録した自分をobserverに教え,observerを登録
    obs->subject_set(this);
    observers_.push_back(obs);
  }

  virtual void detach(observer* obs){
    // 解除した自分をobserverに教え,observerを解除
    obs->subject_unset(this);
    observers_.remove(obs);
  }

  virtual void notify(){
    // Notify!
    std::list<observer*>::iterator it = observers_.begin();
    for(; it != observers_.end(); ++it)
      (*it)->subject_update(this);
  }
};

//
// ConcreteSubjectクラス
//

class data : public subject {

  int n;

public:

  ~data(){
    // 登録しているobserversに自分が消えることを教える(解除)
    std::list<observer*>::iterator it = observers_.begin();
    for(; it != observers_.end(); ++it)
      (*it)->subject_unset(this);
  }

  void set(int x){
    n = x;
    notify();
  }

  int get(){
    return n;
  }
};

//
// ConcreteObserverクラス
//

class viewer : public observer {

  std::list<data*> data_;

public:

  viewer(){}
  ~viewer(){
    // 自分を登録しているsubjectに対し,自分をdetachする
    std::list<data*>::iterator it = data_.begin();
    while(it != data_.end())
      (*it++)->detach(this);
  }

  void subject_update(subject* sub){
    std::list<data*>::iterator it = std::find(data_.begin(), data_.end(), sub);
    assert(it != data_.end()); // dynamic_cast failed!
    std::cout << "data is " << (*it)->get() << std::endl;
  }

  void subject_set(subject* sub){
    assert(std::find(data_.begin(), data_.end(), sub) == data_.end()); // まだ登録されてないはず
    data* new_data = dynamic_cast<data*>(sub);
    assert(new_data != NULL); // dynamic_cast failed!
    data_.push_back(new_data);
  }

  void subject_unset(subject* sub){
    assert(std::find(data_.begin(), data_.end(), sub) != data_.end()); // 登録されているはず
    data_.remove(sub);
  }
};


コメントが無い….


微妙なコメントをちょっとつけた.


これでダングリングにも対応できるかな?


うーん,使用用途にもよるだろうけど,std::vectorの方がいい気がする.


viewer::~viewer()内で無効なイテレータを操作していた.

気付かないうちになっちゃうもんだね(detach内で普通にeraseしてるが).