观察者模式中的常量正确通知程序

发布于 2024-07-16 07:48:35 字数 1565 浏览 10 评论 0原文

我想实现一个模型类的观察者,它不会改变模型。 因此,它应该能够使用 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 或非常量。

干杯,菲利克斯

I want to implement an Observer of a Model class which does not change the Model. Thus, it should be able to use a const-Reference to access the Model. But the Registering of the Observer prohibits this.

Here is how the observer pattern is implemented in my Project:



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

};

The only place where a non-modifying observer needs to "change" the Model is when it wants to register with it. I feel that I can not avoid a const_cast here, but I wanted to know if there are better solutions.

Sidenote:
Put another way, I don't consider the "List of Observers" which a model object manages to be part of the state of the Model. C++ can not tell the difference and lumps state and observers together, forcing both to be const or non-const.

Cheers, Felix

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

暮倦 2024-07-23 07:48:35

如果您认为 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 添加了另一个类),但是您的评论“是非常量,因为它需要返回非常量引用”向我表明您可以完全按照您最初想要的方式进行,你只是没有意识到你可以。

If you consider the Notifier object not to be part of the Model object which owns it, so that modifying the Notifier doesn't "count" as modifying the Model, then make getNotifier a const method returning a non-const reference:

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

     return m_Notifier; 
}

You will then have to either mark m_Notifier as mutable, or else own it by pointer (or smart pointer) or reference rather than by inclusion. Either way, you avoid a const_cast. It is usually preferable to embed objects rather than point/refer to them, but if this is a case where a Notifier isn't considered part of the Model which uses it, then embedding is not essential. Owning it by reference forces you to initialize the reference when Model is constructed, which leads to dependency injection, which is no bad thing. Owning by smart pointer means that, as with embedding, you don't have to do anything about cleanup.

There may be other ways to design things (such as Vinay's addition of another class), but your comment "Is non-const because it needs to return a non-const reference" suggests to me that you can do exactly what you originally wanted, you just don't realise you can.

跨年 2024-07-23 07:48:35

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

I'm not clear from your code, but if you have a member that is logically const, but physically non-const, the usual solution is to make it mutable.

慈悲佛祖 2024-07-23 07:48:35

相反

view->SetModel( model ); 

你可以打电话

model->getNotifier()->addObserver( view );
view->setModel( model ); // this function will accept const Model*

instead

view->SetModel( model ); 

you could call

model->getNotifier()->addObserver( view );
view->setModel( model ); // this function will accept const Model*
束缚m 2024-07-23 07:48:35

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

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

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

类似但不太明显的是,您可以保持原样,但声明 const *Model m_Model。 然后你可以在SetModel中使用aModel作为非常量Model,但是观察者的其他方法都不能修改该Model。

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

An alternative approach to my other answer.

Don't have the observer hold a pointer to the model at all. Pass a const *Model into the update method, which is called by the notifier. This would need to know what model it is notifying for, but that probably isn't difficult given that it's embedded in a model, so it's probably always that one...

If the Observer then needs a non-const Model in SetModel you can still give it one, but more likely is that you'll get rid of SetModel entirely, and just call some_model.AddObserver(some_observer) instead of some_observer.SetModel(some_model).

Similarly but less drastically, you could leave things as they are but declare const *Model m_Model. Then you can use aModel as a non-const Model in SetModel, but no other method of the observer can modify the Model.

Neither of these changes will work if the Observer is expected to be able to unregister itself without a parameter to use to do so.

裂开嘴轻声笑有多痛 2024-07-23 07:48:35

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

Instead of returning the const Model, you can create one more class which wraps the Notifier object and implements Notifier. (Adapter pattern). Observers can use the newly created class for registering/unregistering.

可遇━不可求 2024-07-23 07:48:35

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

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

    }
};

2.更改 MyNonModifingView 以接受 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();
    }

};

I expect Controller to resolve this:

1.Controller knows Model and allows View registration to 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.Change the MyNonModifingView to accept 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();
    }

};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文