(::dump<>):这个模板函数适配器将请求传递给具体的对象。举例来说,在一些情况下,specificRequest的函数签名具有一致性,这不是必须的。但是如果在多个具体的类中,specificRequest有不同的函数签名,SignatureAdapter(签名适配器)将被用来将ConcreteCommon和这些差异隔离开。
4、ConcreteType:ConcreteType定义了specificRequest操作,用于实现希望的功能。虽然具体类是继承无关的,外部多态模式允许你多态的看待它们的全部或一些方法。
协作
外部多态模式被典型地使用在当存在外部客户端通过多态的Common*接口发送请求的情况下。下面是展示这个协作过程的互动图:
一些应用的外部多态模式维护一个对象集合,通过它应用可以使用迭代器统一的对待所有集合中的对象。虽然这并不是这个模式严格要求的一部分,这却是经常提及的最通常的用例。
结论
使用外部多态模式带来如下好处:
1、透明性:最初没有设计成工作在一起的类能够被相对透明的扩展,以至于它们能够被多态的看待。特别是存在类的对象布局没有被增加的虚指针改变。
2、灵活性:多态的扩展不具有扩展能力的数据类型,如int或double,是可能的,当这个模式被一个支持参数化类型的语言实现的时候。
3、外围性:因为这个模式在已经存在类的边界处建立,所以很容易使用条件编译来移除这个模式。对于那些使用外部多态模式用于调试目的的系统中,这个特性是特别有用。
使用外部多态模式有如下缺点:
1、不稳定性:Common和ConcreteCommon中的方法必须跟踪具体类型中方法变化。
2、低效率性:从ConcreteCommon中的虚方法到相应的具体类型对象的相应方法多次调用传递导致额外的负载。但是对于一个单一的虚函数调用,明知的使用inline可以减少这类负载。
使用这个模式需要另外考虑的一个问题:
可能的不一致性:通过指向具体类的指针不能访问外部多态方法。例如,上面的例子中不肯能通过SOCK_Stream的指针来访问dumpable接口中的dump方法。此外也不可能通过ConcreteCommon的对象指针访问具体类型的方法。
实现
当实现这个模式时,将产生下列问题:
1、specificRequest的参数:多态行为的投射需要适配各种specificRequest函数签名。这将是复杂的,例如当一些需要参数,一些不需要参数时。实现必须确定是否在多态接口中暴露这些参数还是将它们与用户隔离。
2、代码跑到哪里去了?正像前面所提到的,外部的类层次必须和原始的类保持并行。实现必须小心的选择源文件,在这里协作的代码将被实现,如函数签名的适配。
3、外部模式不是适配器:适配器模式的目的是转换一个接口成用户可以使用的接口。另一个方面,外部多态模式为存在的接口强行提供一个新的基接口。
例子代码
Dumpable类构成层次的基础,定义了需要的多态接口,在这个例子中是用于导出信息:
class Dumpable
{
public:
Dumpable (const void *);
// This pure virtual method must be
// implemented by a subclass.
virtual void dump (void) const = 0;
};
ObjectCollection是一个简单的客户集合用于存放所有对象。这个类是基于STL中的victor来实现的:
class ObjectCollection : public vector
{
public:
// Iterates through the entire set of
// registered objects and dumps their state.
void dump_objects (void);
};
dump_objects函数的实现如下:
void ObjectCollection::dump_objects (void)
{
struct DumpObject {
bool operator()(const Dumpable*& dp) {
dp->dump();
}
};
for_each(begin(), end(), DumpObject());
}
现在基础已经完成,我们建立ConcreteDumpable:
template
class ConcreteDumpable : public Dumpable
{
public:
ConcreteDumpable (const ConcreteType* t);
virtual void dump (void) const;
// Concrete dump method
private:
const ConcreteType* realThis_;
// Pointer to actual object
};
ConcreteDumpable 方法实现如下:
template
ConcreteDumpable
::ConcreteDumpable
(const ConcreteType* t): realThis_ (t)
{
}
template
void
ConcreteDumpable
::dump (void) const
{
dump
(realThis_);
}
现在只剩下签名适配器了。假设SOCK_Stream 和
SOCK_Acceptor都拥有一个dump方法使用cerr来输出信息。另一方面INET_Addr拥有一个printTo方法,该方法使用输出流作为唯一的参数。我们将定义两个签名适配器,一个是泛化的签名适配器,可以适配任何具有dump方法的具体类型:
template
void
dump
(const ConcreteType* t)
{
t->dump();
}
但是,第二个是特化的签名适配器,用于客户化INET_Addr:
void dump
(const INET_Addr* t)
{
t->printTo(cerr);
}
ObjectCollection实例可以被ConcreteDumpable<>实例来填充:
...
oc.insert(oc.end(), aSockStream);
oc.insert(oc.end(), aSockAcceptor);
oc.insert(oc.end(), aInetAddr);
oc.dump_objects();
相关模式
这个模式与GOF提出的适配器模式和装饰模式非常相似。装饰模式动态的扩展一个对象而不是使用子类化的方式。当用户使用装饰对象时,它将认为它操作的是实际的对象,但是事实上它操作的是装饰对象。适配器模式是转化一个接口使其变成客户需要的接口。适配器模式使那些具有不兼容接口的类能够工作在一起。
在GOF模式和外部多态模式之间有几点不同。装饰模式总是假定其装饰的类已经是抽象的。相反,外部多态模式为具体的类增加多态。此外,因为装饰器继承自它所装饰的类,它必须重新定义所有继承的方法。相反,外部多态模式中的ConcreteCommon类仅仅定义具体类中想要被多态处理的方法。
外部多态模式与GOF的适配器模式相似,但是有几个微妙但重要的差别:
1、
意图不同:适配器转化接口使其可以被客户直接使用。外部多态没有转换接口的本质动机,而是提供一个用于访问相似的功能的新的底层。
2、
上下层次对水平层次:外部多态模式在存在的具体类外层创建一个完整的类层次,适配器在存在的类层次内创建新的层次。
3、
扩展对转换:外部多态模式扩展存在的接口,使相似的功能能够被多态的访问。适配器则是创建新的接口。
4、
行为对接口:外部多态模式重要是关注自己的行为而不是和特定行为相关的名字。