代码之家  ›  专栏  ›  技术社区  ›  TheFogger

在观察者模式中构造正确的通知器

  •  2
  • TheFogger  · 技术社区  · 17 年前

    
    
    //Attributes of type Observable are used by classes that want to notify others
    //of state changes. Observing Objects register themselves with AddObserver.
    //The Observable Object calls NotifyObservers when necessary.
    class Notifier
    {
    public:
        AddObserver(Observer*);
        RemoveObserver(Observer*);
        NotifyObservers();
    };
    
    class Model
    {
    public:
        Notifier& GetNotifier() //Is non const because it needs to return a non-const 
        {                       //reference to allow Observers to register themselves.
    
             return m_Notifier; 
        }
    
        int QueryState() const;
    
        void ChangeModel(int newState)
        {
            m_Notifier.NotifyObservers();
        }
    
    private:
        Notifier m_Notifier;
    };
    
    //This View does not Modify the Model.
    class MyNonModifingView : public Observer
    {
    public:
        SetModel(Model* aModel)  //should be const Model* aModel...
        {
            m_Model = aModel;
            m_Model->GetNotifier().AddObserver(this); //...but can't because
            //SetModel needs to call GetNotifier and add itself, which requires
            //non-const AddObserver and GetNotifier methods.
        }
    
        void Update()  //Part of Observer-Interface, called by Notifiers
        {
            m_Model->QueryState();
        }
    
    };
    
    

    附注:

    6 回复  |  直到 17 年前
        1
  •  5
  •   Steve Jessop    17 年前

    如果您认为Notifier对象不是拥有它的Model对象的一部分,因此修改Notifier不会“算作”修改Model,那么将getNotifier设置为一个const方法,返回一个非const引用:

    Notifier& GetNotifier() const //Is const but returns a non-const 
    {                             //reference to allow Observers to 
                                  //register themselves.
    
         return m_Notifier; 
    }
    

    然后,您必须将m_Notifier标记为可变,或者通过指针(或智能指针)或引用而不是包含来拥有它。无论哪种方式,您都可以避免使用const_cast。通常最好嵌入对象而不是指向/引用它们,但如果在这种情况下,通知器不被视为使用它的模型的一部分,那么嵌入就不是必需的。通过引用拥有它会迫使您在构建模型时初始化引用,这会导致依赖注入,这并不是一件坏事。智能指针拥有意味着,与嵌入一样,你不必做任何清理工作。

    可能还有其他的设计方法(比如Vinay添加了另一个类),但你的评论“Is non const,因为它需要返回一个非const引用”告诉我,你可以做你最初想做的事情,你只是没有意识到你可以做到。

        2
  •  3
  •   anon anon    17 年前

    我从你的代码中不清楚,但如果你有一个逻辑上为const但物理上为非const的成员,通常的解决方案是将其设置为 可变的 .

        3
  •  1
  •   bayda    17 年前

    相反

    view->SetModel( model ); 
    

    你可以打电话

    model->getNotifier()->addObserver( view );
    view->setModel( model ); // this function will accept const Model*
    
        4
  •  1
  •   xtofl Adam Rosenfield    17 年前

    我另一个答案的替代方法。

    根本不要让观察者拿着指向模型的指针。通过a const *Model 通知程序调用update方法。这需要知道它通知的是什么模型,但考虑到它嵌入在模型中,这可能并不难,所以它可能总是那个模型。..

    如果观察者在SetModel中需要一个非const模型,你仍然可以给它一个,但更有可能的是,你会完全摆脱SetModel,只需调用 some_model.AddObserver(some_observer) 而不是 some_observer.SetModel(some_model) .

    同样,但不那么彻底,你可以保持现状,但要声明 const *Model m_Model 。然后,您可以在SetModel中将aModel用作非常量模型,但观察者的其他方法无法修改该模型。

    如果预期观察者能够在没有参数的情况下注销自己,则这些更改都不会起作用。

        5
  •  0
  •   Vinay    17 年前

    您可以创建一个额外的类来封装Notifier对象并实现Notifier,而不是返回const Model。(适配器模式)。观察者可以使用新创建的类进行注册/注销。

        6
  •  0
  •   aJ.    17 年前

    我希望控制员能够解决这个问题:

    1.控制器知道模型,并允许视图注册到模型。

    class MyController 
    {
    public:
    
        //Controller associated with the Model
        MyController(Model* pModel):m_pModel(pModel)
        {
        }
    
        //Provide the facility to register the view. 
        //Alternatively, if there is 1:1 relation between controller and View then View poniter can be stored locally inside Controller
        void registerObserver(Observer* pObserver) 
        {
                   //Register observer  
            m_pModel->GetNotifier().AddObserver(pObserver);
                   //set the model in view
            pObserver->SetModel(m_pModel);
    
        }
    };
    

    class MyNonModifingView : public Observer
    {
    public:
        SetModel(const Model* aModel) 
        {
            m_Model = aModel;
        //NO need to register here, My controller does it for me.
            //   m_Model->GetNotifier().AddObserver(this);
        }
    
        void Update()  //Part of Observer-Interface, called by Notifiers
        {
            m_Model->QueryState();
        }
    
    };