2

我想实现一个不改变模型的模型类的观察者。因此,它应该能够使用 const-Reference 来访问模型。但观察员登记禁止这样做。

以下是我的项目中如何实现观察者模式:



//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();
    }

};

非修改观察者需要“更改”模型的唯一地方是它想要注册它的时候。我觉得我无法避免 const_cast 这里,但我想知道是否有更好的解决方案。

旁注:换句话说,我不认为模型对象管理的“观察者列表”是模型状态的一部分。C++ 无法区分并将状态和观察者混为一谈,迫使两者都是 const 或 non-const。

干杯,菲利克斯

4

6 回答 6

5

如果您认为 Notifier 对象不是拥有它的 Model 对象的一部分,因此修改 Notifier 不会“算作”修改 Model,那么将 getNotifier 设置为返回非常量引用的 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 添加另一个类),但是您的评论“是非常量,因为它需要返回非常量引用”向我表明您可以完全按照您的意愿去做,你只是没有意识到你可以。

于 2009-04-07T12:48:15.470 回答
3

我从您的代码中不清楚,但如果您有一个逻辑上为 const 但物理上非 const 的成员,通常的解决方案是使其mutable

于 2009-04-07T12:18:18.223 回答
1

反而

view->SetModel( model ); 

你可以打电话

model->getNotifier()->addObserver( view );
view->setModel( model ); // this function will accept const Model*
于 2009-04-07T12:42:34.530 回答
1

我的其他答案的另一种方法。

根本不要让观察者持有指向模型的指针。将 a 传递给const *Model由通知程序调用的更新方法。这需要知道它正在通知什么模型,但这可能并不难,因为它嵌入在模型中,所以它可能总是那个......

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

同样,但不那么彻底,您可以保持原样,但要声明const *Model m_Model。然后你可以在SetModel中使用aModel作为一个非常量Model,但是观察者没有其他方法可以修改这个Model。

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

于 2009-04-07T13:00:49.993 回答
0

除了返回 const 模型,您还可以再创建一个类来包装 Notifier 对象并实现 Notifier。(适配器模式)。观察者可以使用新创建的类进行注册/注销。

于 2009-04-07T12:20:37.250 回答
0

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

1.Controller知道Model并允许View注册到Model。

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);

    }
};

2.更改 MyNonModifiningView 以接受 const Model* aModel

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

};
于 2009-04-07T13:18:13.197 回答