看来您已经决定了对象之间的某种关系。但是,你只是模糊地描述了这种关系。
如果RegisterALL
是使用简单的遏制,那么你将有一个非常简单的关系。这种关系可以这样表达(请原谅 ASCII 图形):
+-------------+
| RegisterALL | --> := has
+-------------+
| |
v v
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
优点是两个家属的照片非常简单。但是,如果您要注册许多对象,则图片开始看起来像是RegisterALL
爆炸成一堆XRegister
对象。
如果RegisterALL
要包含ARegister
and BRegister
,您可能希望为 and 创建一个公共基类ARegister
,BRegister
以便RegisterALL
可以维护容器。
+-------------+ +------------------+ <>--> := aggregates
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+ |
| _/_\_ := inherits
/ \
___/___\___
| |
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
我们看到,无论有多少新项目被注册, 和 之间的关系都RegisterALL
保持AbstractRegister
不变。我们可以更进一步,从模板中派生ARegister
和派生。BRegister
+-------------+ +------------------+
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+
|
/ \
/___\
|
| +--------------+
+--------------| RegisterType |
| +--------------+
| RegisterTemplate |
+------------------+
好的,OO 设计课就讲这么多。这可以很快转化为代码。让我们从简单的事情开始。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
被实现为单例,这意味着它只允许它自己的一个实例。该add
方法执行注册,并且该find
方法允许找到已注册的实例。构造函数的实现RegisterALL
被推迟到 a 的定义之后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
并由 a 参数化RegisterType
。它type
通过返回其模板参数的值来实现虚方法。它也使用单例,但不公开其实例。相反,它的实例由RegisterALL
. 它提供了向register_type
注册自己的方法RegisterALL
,而这个实例只能通过find
on 的方法找到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
请注意智能指针如何为我们自动清理已注册的项目。