我想实现一个Model类的Observer,它不会改变Model。因此,它应该能够使用const-Reference来访问Model。但注册观察员禁止这样做。
以下是我的项目中实现观察者模式的方式:
//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或非const。
干杯,菲利克斯
答案 0 :(得分:5)
如果您认为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。通常最好嵌入对象而不是指向/引用它们,但如果这是Notifier不被认为是使用它的模型的一部分的情况,则嵌入不是必需的。通过引用拥有它会强制您在构造Model时初始化引用,这会导致依赖注入,这不是坏事。拥有智能指针意味着,与嵌入一样,您不必对清理做任何事情。
可能还有其他设计方法(例如Vinay添加了另一个类),但是你的评论“非const是因为它需要返回一个非const引用”,这表明你可以完全按照你的意思去做最初想要的,你只是没有意识到你可以。
答案 1 :(得分:3)
我不清楚你的代码,但是如果你有一个逻辑上是const的成员,但是物理上是非const的,那么通常的解决办法就是让它可变。
答案 2 :(得分:1)
而不是
view->SetModel( model );
你可以打电话
model->getNotifier()->addObserver( view );
view->setModel( model ); // this function will accept const Model*
答案 3 :(得分:1)
另一种解决方法。
根本没有观察者手指指向模型的指针。将const *Model
传递给更新方法,该方法由通知程序调用。这需要知道它通知的是什么模型,但这可能并不困难,因为它嵌入在模型中,所以它可能总是那个......
如果Observer在SetModel中需要一个非const模型,你仍然可以给它一个,但更有可能的是你将完全摆脱SetModel,只需调用some_model.AddObserver(some_observer)
而不是{{1} }。
同样但不那么剧烈,你可以保留原样,但声明some_observer.SetModel(some_model)
。然后你可以在SetModel中使用aModel作为非const模型,但观察者的其他方法都不能修改模型。
如果希望Observer能够在没有用于执行此操作的参数的情况下取消注册,则这些更改都不会起作用。
答案 4 :(得分:0)
您可以创建另一个包装Notifier对象并实现Notifier的类,而不是返回const Model。 (适配器模式)。观察者可以使用新创建的类进行注册/取消注册。
答案 5 :(得分:0)
我希望Controller解决这个问题:
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.更改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();
}
};