2017-03-15 20 views
-1

設定ファイルSettingsControllerを管理するクラスがあります。SettingsClient(1つの純粋仮想関数、メンバーなし)を登録することができます。関連する設定エントリが更新されるように変更されたときには、SettingsClientに通知されます。継承と組み合わせてObserverパターンを実現するにはどうすればよいですか?

今、私はいくつかの設定項目を必要とするため、SettingsClientとレジスタSettingsControllerでも、いくつかの設定項目を必要とし、コントローラでクライアントとレジスタを継承し、具体的なリポジトリConcreteRepositoryを継承RepositoryBaseを持っています。

ConcreteRepositoryRepositoryBaseを継承し、両方が(JavaでConcreteRepositoryRepositoryBaseextendなり、両方がSettingsClientimplementなる)コントローラに登録することができるようにSettingsClientを継承します。

私の問題は次のとおりです。コンパイラはあいまいであるため、これをしないよう警告します。残念ながら仮想継承は、ConcreteRepositoryまたはRepositoryBaseの実装された関数を上書きし、2つのクラスのうちの1つのリフレッシュ機能を無効にするため、ここでは役に立ちません。

この継承とオブザーバーの組み合わせを実現する方法はありますか?私は、これはConcreteRepositoryのメンバーに設計上の欠陥とRepositoryBaseニーズかもしれません考えています

をここで概要を与えるためにいくつかのコードである(?):

#include <vector> 
#include <iostream> 

class SettingsClient { 
public: 
    virtual void reloadSettings() = 0; 
}; 

class SettingsController { 
    void notify(){ 
    for(SettingsClient* client : clients){ 
     client->reloadSettings(); // error! reloadSettings() of RepositoryBase or ConcreteRepository? 
    } 
    } 
    void registerClient(SettingsClient *client) { 
    clients.push_back(client); 
    } 

    std::vector<SettingsClient*> clients; 
}; 

class RepositoryBase : private SettingsClient { 
    // ... 
    virtual void reloadSettings() { 
    std::cout << "Reloading Base!" << "\n"; 
    } 
    // ... 
}; 

class ConcreteRepository : private SettingsClient, private RepositoryBase { 
    // ... 
    virtual void reloadSettings() { 
    std::cout << "Reloading ConcreteRepository!" << "\n"; 
    } 
    // ... 
}; 
+0

公開されているので、SettingsClientは仮想デストラクターが必要です。ConcreteRepositoryは、RepositoryBase経由で既に設定されているため、SettingsClientを継承する必要はありません。必要に応じてRepositoryBaseのreloadSettingsを明示的に呼び出します。 C++には、オーバーライドされたメンバーを複数の基本クラスを呼び出すようにデフォルトするメカニズムはありません。 – evan

答えて

1

も継承するConcreteRepositoryための必要はありませんSettingsClientから。あなたはベースクラスのコンストラクタにリポジトリを一度登録してから、ベースクラスreloadSettingsをサブクラスのバージョンから呼び出すことができます。私たちは、サブクラスで公開さであることからreloadSettingsを防ぐためにSettingsClientからプライベート継承を使用

class RepositoryBase : private SettingsClient 
{ 
public: 
    RepositoryBase(SettingsController& controller) 
    { 
    controller.registerClient(this); 
    } 

private: 
    void reloadSettings() override 
    { 
    std::cout << "Reloading RepositoryBase\n"; 
    } 
}; 

class ConcreteRepository : public RepositoryBase 
{ 
public: 
    using RepositoryBase::RepositoryBase; 

private: 
    void reloadSettings() override 
    { 
    std::cout << "Reloading ConcreteRepository\n"; 
    } 
}; 

。基本クラスの関数を呼び出す必要がある場合は、プロテクトの継承を代わりに使用することができます。

class RepositoryBase : protected SettingsClient 
{ 
public: 
    RepositoryBase(SettingsController& controller) 
    { 
    controller.registerClient(this); 
    } 

protected: 
    void reloadSettings() override 
    { 
    std::cout << "Reloading RepositoryBase\n"; 
    } 
}; 

class ConcreteRepository : public RepositoryBase 
{ 
public: 
    using RepositoryBase::RepositoryBase; 

protected: 
    void reloadSettings() override 
    { 
    RepositoryBase::reloadSettings(); 
    std::cout << "Reloading ConcreteRepository\n"; 
    } 
}; 

基本クラスとサブクラスに2つの別個のクライアントを登録する必要がある、または好む場合は、構図を使用できます。

class RepositoryBase 
{ 
public: 
    RepositoryBase(SettingsController& controller) 
    { 
    controller.registerClient(&client); 
    } 

private: 
    struct : public SettingsClient 
    { 
    void reloadSettings() override 
    { 
     std::cout << "Reloading RepositoryBase\n"; 
    } 
    } client; 
}; 

class ConcreteRepository : public RepositoryBase 
{ 
public: 
    ConcreteRepository(SettingsController& controller) : RepositoryBase(controller) 
    { 
    controller.registerClient(&client); 
    } 

private: 
    struct : public SettingsClient 
    { 
    void reloadSettings() override 
    { 
     std::cout << "Reloading ConcreteRepository\n"; 
    } 
    } client; 
}; 

非スタティックメンバーにアクセスする必要がある場合、クライアントにリポジトリへのポインタを与えることができます。

関連する問題