代码之家  ›  专栏  ›  技术社区  ›  Cheok Yan Cheng

我应该在带有模板的主题观察者模式中使用动态强制转换吗

  •  1
  • Cheok Yan Cheng  · 技术社区  · 15 年前

    通过参考文章 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 ?

    静态铸造 ,在以下情况下,我将得到编译错误。

    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() {
        }
    }
    

    使用有什么副作用吗 动态铸件 ?

    2 回复  |  直到 15 年前
        1
  •  4
  •   sbi    15 年前

    最好的肯定是根本不需要铸造。你可以改变你的 notify() 函数,使其采用正确的参数:

      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 .


    看看你的代码 static_cast 依赖于这样一个事实 Observer ,也将从作为模板参数传递的任何内容派生。我想如果这个不成立,它会在编译时被捕获,因为你不能 静态铸造 T* .

    但是,您的代码非常接近一个称为奇怪的重复模板模式的模式。为了使其完全适合,请将派生类“type”传递给 观察者 :

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

    现在你不需要从 观察者 T

        2
  •  2
  •   David Rodríguez - dribeas    15 年前

    在一条相切的推理线上,您可以使用现有的库来实现这一点,比如 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 S可以观察动物,或者 观察 具有具体的签名/名称。同时,如果您的 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;
    }
    
    推荐文章