ObserverパターンでのConst、正しい通知
-
05-09-2019 - |
質問
私はモデルを変更しないモデルクラスのオブザーバーを実装します。したがって、モデルにアクセスするためのconstリファレンスを使用することができるはずです。しかし、オブザーバーの登録はこれを禁止します。
ここでは、オブザーバーパターンは私のプロジェクトで実装されている方法です。
//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のように両方を強制的に、一緒に差塊状態とオブザーバーを伝えることはできません。
乾杯、フェリックス
解決
あなたが通知を変更すると、その後、モデルの変更などの「カウント」は非const参照を返す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を避けてください。それらを参照/むしろ点よりオブジェクトを埋め込むことが通常好ましいが、これは、それを使用するモデルの一部通知を考慮しない場合であれば、その後の埋め込みは必須ではありません。何も悪いことではないの注射を、依存関係につながる、モデルが構築されるときに参照を初期化するために、参照力によってそれを所有しています。スマートポインタで所有している埋め込むと同じように、あなたはクリーンアップについて何もする必要はありません、ということを意味します。
(例えば、別のクラスのビナイの追加など)のものを設計する他の方法があるかもしれませんが、あなたのコメントは、「それは非const参照を返す必要があるため、非constです」あなたは正確に何を行うことができますことを私に示唆もともとあなただけのことができます気付いていない、望んでいた。
他のヒント
私はあなたのコードから明確でないんだけど、あなたは論理的にconstのあるメンバーが、物理的に非constを持っている場合、通常の解決策は、それを作るためにあるの可変の。
ではなく
view->SetModel( model );
あなたが呼び出すことができます。
model->getNotifier()->addObserver( view );
view->setModel( model ); // this function will accept const Model*
私の他の回答に代わるアプローチます。
観察者は全くのモデルへのポインタを保持していないでください。通知によって呼び出されるupdateメソッドへconst *Model
を渡します。これは、それがために通知しているもののモデルを知る必要があるだろうが、それはおそらく、それがモデルに埋め込まれていますことを考えれば難しいことではありませんので、それは常に、おそらくその一つだ...
は、あなたの代わりにsome_model.AddObserver(some_observer)
のsome_observer.SetModel(some_model)
をまだそれを1つずつ与えるが、より多くの可能性が高いあなたは完全に使ってsetModelを取り除くだろうということで、単に呼び出すことができます。
同様に、あまり大幅に、あなたは彼らがそうであるように、物事を残すが、const *Model m_Model
を宣言することができます。そして、あなたが使ってsetModelで非constモデルとしてアモデルを使用することができますが、観察者の他の方法は、モデルを変更することはできません。
これらの変化はいずれも、オブザーバーがそうするために使用するパラメータを指定せずに自分自身を登録解除することができると期待されている場合に動作します。
の代わりにconstのモデルを返すには、通知オブジェクトをラップし、通知を実装して1つの以上のクラスを作成することができます。 (アダプタパターン)。オブザーバーは、登録/登録解除のために、新しく作成したクラスを使用することができます。
私は、コントローラがこの問題を解決することを期待します
1.Controllerはモデルを知っているし、モデルにビュー登録することができます。
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 constのモデルを受け入れるMyNonModifingView *アモデル
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();
}
};