オブジェクト間の何らかの関係はすでに決まっているようです。しかし、あなたは漠然と関係を説明するだけです。
が単純な封じ込めを使用している場合RegisterALL
、非常に単純な関係になります。この関係は次のように表現される場合があります(ASCIIグラフィックを許してください)。
+-------------+
| RegisterALL | --> := has
+-------------+
| |
v v
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
利点は、2人の扶養家族の写真が非常に単純なことです。ただし、多くのオブジェクトを登録している場合、画像はオブジェクトRegisterALL
の束に爆発しているように見え始めXRegister
ます。
RegisterALL
がとを含むことを意図している場合は、コンテナを維持できるように、とARegister
のBRegister
共通基本クラスを作成することをお勧めします。ARegister
BRegister
RegisterALL
+-------------+ +------------------+ <>--> := aggregates
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+ |
| _/_\_ := inherits
/ \
___/___\___
| |
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
RegisterALL
いくつの新しいアイテムが登録されても、との間の関係AbstractRegister
は同じままであることがわかります。さらに一歩進んで、テンプレートから派生することができARegister
ますBRegister
。
+-------------+ +------------------+
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+
|
/ \
/___\
|
| +--------------+
+--------------| RegisterType |
| +--------------+
| RegisterTemplate |
+------------------+
さて、オブジェクト指向デザインのレッスンはこれで終わりです。これは非常に迅速にコードに変換されます。簡単なことから始めましょう。RegisterType
登録するさまざまなものを列挙します。RegisterTypeName
オーバーロードされた演算子を使用すると、コードは、を出力するときに数値ではなく文字列を出力できますRegisterType
。
enum RegisterType { A, B, MAX_RegisterType };
static inline std::string
RegisterTypeName (RegisterType t)
{
static const char * names[] = { "A", "B" };
return names[t];
}
static inline std::ostream &
operator << (std::ostream &output, RegisterType t)
{
return output << RegisterTypeName(t);
}
AbstractRegister
このタイプを照会するためのインターフェースを提供します。さらに、poke
インターフェイスにはデフォルトの実装が用意されています。C ++では、抽象型は仮想デストラクタを提供する必要があることに注意してください。
class AbstractRegister {
public:
virtual ~AbstractRegister () {}
virtual RegisterType type () const = 0;
virtual void poke () { std::cout << "Poked " << type(); }
};
typedef std::unique_ptr<AbstractRegister> AbstractRegisterPtr;
static const AbstractRegisterPtr AbstractRegisterNullPtr;
RegisterALL
クラスには、タイプのものを保持するためのコンテナがありますAbstractRegister
。マップを使用して、登録と見なしRegisterType
ているAbstractRegister
インスタンスに関連付けます。RegisterALL
シングルトンとして実装されます。つまり、それ自体のインスタンスは1つだけ許可されます。このadd
メソッドは登録を実行し、このメソッドはfind
登録されたインスタンスを見つけることを可能にします。コンストラクターの実装は、RegisterALL
の定義が完了するまで延期されRegisterTemplate
ます。
class RegisterALL {
template <RegisterType> friend class RegisterTemplate;
typedef std::unique_ptr<RegisterALL> SelfPtr;
typedef std::map<RegisterType, AbstractRegisterPtr> RegisterMap;
void add (AbstractRegister *r) { all[r->type()] = AbstractRegisterPtr(r); }
RegisterALL ();
public:
static const SelfPtr & instance () {
if (!one) new RegisterALL;
return one;
}
const AbstractRegisterPtr & find (RegisterType t) const {
RegisterMap::const_iterator i = all.find(t);
return (i != all.end()) ? i->second : AbstractRegisterNullPtr;
}
private:
static SelfPtr one;
RegisterMap all;
};
RegisterALL::SelfPtr RegisterALL::one;
RegisterTemplate
クラスはから派生し、によってAbstractRegister
パラメータ化されますRegisterType
。type
テンプレートパラメータの値を返すことにより、仮想メソッドを実装します。また、シングルトンを採用していますが、インスタンスを公開していません。代わりに、そのインスタンスはによって管理されRegisterALL
ます。register_type
に自分自身を登録するメソッドを提供します。このインスタンスは、のメソッドをRegisterALL
使用することによってのみ見つけることができます。find
RegisterALL
template <RegisterType RT>
class RegisterTemplate : public AbstractRegister {
RegisterType type () const { return RT; }
void poke () {
std::cout << "Poked " << RegisterTypeName(RT) << std::endl;
}
RegisterTemplate () {
std::cout << "Created " << RegisterTypeName(RT) << std::endl;
}
~RegisterTemplate () {
std::cout << "Destroying " << RegisterTypeName(RT) << std::endl;
}
public:
static void register_type () {
if (RegisterALL::instance()->find(RT)) {
std::cout << "Already registered " << RegisterTypeName(RT)
<< std::endl;
return;
}
RegisterALL::instance()->add(new RegisterTemplate<RT>);
}
};
コンストラクターは、列挙型を反復処理するRegisterALL
ヘルパーテンプレートを使用し、対応するをインスタンス化することで、すべてのをに登録します。register_all
RegisterType
RegisterTemplate
RegisterType
RegisterALL
template <unsigned X>
struct register_all {
register_all () {
RegisterTemplate<static_cast<RegisterType>(X)>::register_type();
register_all<X+1>();
}
};
template <> struct register_all<MAX_RegisterType> {};
inline RegisterALL::RegisterALL ()
{
one = std::move(SelfPtr(this));
register_all<0>();
}
したがって、試してみるには、次のコードを使用します。
RegisterALL::instance(); // registers all RegisterType's
RegisterTemplate<B>::register_type(); // try to register B again
RegisterALL::instance()->find(A)->poke(); // poke at A
そしてこれは出力です:
Created A
Created B
Already registered B
Poked A
Destroying B
Destroying A
スマートポインタが登録済みのアイテムを自動的にクリーンアップする方法に注目してください。