2010-10-20 39 views
1

主题观察者模式动态转换通过参考文章Implementing a Subject/Observer pattern with templates我应该使用与模板

template <class T> 
class Observer 
    { 
    public: 
     Observer() {} 
     virtual ~Observer() {} 
     virtual void update(T *subject)= 0; 
    }; 

template <class T> 
class Subject 
    { 
    public: 
     Subject() {} 
     virtual ~Subject() {} 
     void attach (Observer<T> &observer) 
     { 
     m_observers.push_back(&observer); 
     } 
     void notify() 
     { 
     std::vector<Observer<T> *>::iterator it; 
     for (it=m_observers.begin();it!=m_observers.end();it++) 
       (*it)->update(static_cast<T *>(this)); 
     } 
    private: 
     std::vector<Observer<T> *> m_observers; 
    }; 

我想知道的,而不是static_cast,我将使用dynamic_cast

这是因为如果我使用static_cast,在以下情况下会出现编译错误。

class Zoo : public Observer<Animal> { 
public: 
    Zoo() { 
     animal = new Bird(); 
     animal->attach(this); 
    } 

    virtual ~Zoo() { 
    delete animal; 
    } 

    virtual void update(Animal* subject) { 
    } 

    Animal* animal; 
} 

// If using static_cast, compilation error will happen here. 
class Bird : public Animal, public Subject<Animal> { 
public: 
    virtual ~Bird() { 
    } 
} 

使用dynamic_cast有什么副作用吗?

回答

4

最好的肯定是不必投。

void notify (T* obj) 
    { 
    std::vector<Observer<T> *>::iterator it; 
    for (it=m_observers.begin();it!=m_observers.end();it++) 
      (*it)->update(obj); 
    } 

现在,派生类可以通过合适的对象(this,如果合适的话),而基类不需要知道派生类的以T的关系:所以,它需要正确的参数,你可以改变你的notify()功能。在你的代码


寻找,因为它是,static_cast依赖,无论从Observer派生的事实,也将获得无论从任何它通过为模板参数。我认为如果这不能成立,它会在编译时被捕获,因为你不能从到T*

但是,您的代码非常接近称为好奇循环模板模式的模式。为了使它完全适合,通过派生类的类型Observer

class Bird : public Subject<Bird> // note the template argument 

现在你不需要从Observer派生的T了和谁看它(希望)识别模式和理解代码更容易。

2

推理的切线,可以使用现有的库对于这一点,像boost::signal,让你定义一个事件和连接听众(观察者),以该事件。

// Forgive the lack of encapsulation and const-correctness to keep the example simple: 
struct Animal { 
    boost::signal< void (Animal&)> signal_update; 
    std::string name; 
}; 
class Bird : public Animal { 
public: 
    void rename(std::string const & n) { 
     name = n; 
     signal_update(*this); 
    } 
}; 
class Zoo 
{ 
public: 
    Zoo() : bird() { 
     bird.signal_update.connect(boost::bind(&Zoo::an_animal_changed, this, _1)); 
    } 
    void an_animal_changed(Animal & a) { 
     std::cout << "New name is " << a.name << std::endl; 
    } 
    Bird bird; 
}; 
int main() { 
    Zoo zoo; 
    zoo.bird.rename("Tweety"); // New name is Tweety 
} 

该解决方案的优点(和缺点)是它放松了观察者和主体之间的耦合。这意味着你不能强制只有Zoo可以观察动物,或者观察的方法有一个具体的签名/名称。这是在同一时间的优势,如果你Animal不知道或不关心是谁在观察:

class Scientist { 
public: 
    Scientist(Zoo & zoo) 
    { 
     zoo.bird.signal_update.connect(boost::bind(&Scientist::study, this, _1)); 
    } 
    void study(Animal & a) { 
     std::cout << "Interesting specimen this " << a.name << std::endl; 
    } 
}; 
int main() { 
    Zoo zoo; 
    Scientist pete(zoo); 
    zoo.bird.rename("Tweety"); // New name is: Tweety 
           // Interesting specimen this Tweety 
} 

注意的类型和函数名可以通过boost::bind进行调整。如果科学家在两个动物园中工作,甚至可以通知哪个动物园更换属于哪个动物园:

// [skipped]: added a name to the zoo 
void Scientist::work_at(Zoo & zoo) { 
    zoo.bird.signal_update.connect(boost::bind(&Scientist::study, this, _1, zoo.name)); 
} 
// updated signature: 
void Scientist::study(Animal & a, std::string const & zoo_name) 
{ 
    std::cout << "Interesting animal " << a.name << " in zoo " << zoo_name << std::endl; 
} 
相关问题