Bridge模式与仿函数
1. STL中仿函数的传递
Ø 在STL中,函数对象(即仿函数)以值传递和返回,所以必须确保当那么传递(也就是拷贝)时你的函数对象行为良好。这暗示了两个东西:
1) 你的函数对象应该很小。否则它们的拷贝会很昂贵;
2) 你的函数对象必须单态(也就是,非多态),即不能使用虚函数。否则会造成切割问题(因为派生类对象以值传递代入基类类型的参数造成切割)。
Ø 问题:并不是所有的仿函数都是小的、单态的,有些函数对象自然是很重的,而且禁止多态仿函数也是不切实际的。有什么方法可以让大的和/或多态的函数对象仍然允许它们以值传递的方式遍布STL吗?答案是采用Bridge模式:带着你要放进你的仿函数类(即Bridge模式中的Abstration类)的数据和/或多态,把它们移到另一个类(即Bridge模式中的Implementor类)中,然后给你的仿函数一个指向这个新类的指针。
Ø 示例:
1) 比如你想要建立一个包含很多数据的多态仿函数类:
Template
Class BPFC : public unary_function
{
private:
Widget w; //表示这个类有很多数据,
Int x; //所以用值传递
… //会影响效率
Public:
Virtual void operator()(const T& val) const; //表示这是一个多态类,
… //所以切割时会出问题
};
如果以值传递该函数对象,不仅拷贝会很昂贵,而且会造成切割问题。解决办法如下:
2) 建立一个包含一个指向实现类的指针的小而单态的类(BPFC),然后把所有数据和虚函数放到实现类(BPFCImpl):
Template
Class BPFCImpl : public unary_function
{
//数据成员
Private:
Widget w; //以前在BPFC里面的所有数据
Int x; //现在在这里
…
Virtual ~BPFCImpl();
Virtual void operator()(const T& val) const;
Friend class BPFC; //让BPFC可以访问这些数据
};
Template
Class BPFC : public unary_function //小的、单态版的BPFC
{
Private:
BPFCImpl *pImpl; //这是BPFC唯一的数据
Public:
Void operator()(const T& val) const //现在非虚;
{ //调用BPFCImpl的虚函数
pImpl->operator()(val);
}
};
采用Bridge模式,BPFC::operator()的实现例证了BPFC所有的虚函数是怎么实现的:它们调用了在BPFCImpl中它们真的虚函数。结果是仿函数类(BPFC)是小而单态的,但可以访问大量状态而且行为多态。
2. Bridge(桥接)模式
Ø 意图:将抽象部分与它的实现部分分离,使它们可以独立地变化。
Ø 别名:Handle/Body
Ø 结构:
Client |
Abstraction Operation() |
Implementor Operationimp() |
Imp->Operationimp(); |
imp |
RefinedAbstraction |
ConcreteimplementorA Operatrionimp() |
ConcreteimplementorB Operationimp() |
Ø 参与者
1) Abstraction
² 定义抽象类的接口。
² 维护一个指向Implementor类型对象的指针。
2) RefinedAbstraction
² 扩充由Abstraction定义的接口。
3) Implementor
² 定义实现类的接口,该接口不一定要与Abstraction的接口完全一致;事实上这两个接口可以完全不同。一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些基本操作的较高层次的操作。
4) ConcreteImplementor
² 实现Implementor接口并定义它的具体实现。
Ø 协作:Abstraction将client的请求转发给它的Implementor对象。
Ø 效果(优点):
1)分离接口及其实现部分
² 一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。
² 将Abstraction与Implementor分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类时,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间二进制兼容性,一定要有这个性质。
² 接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道Abstraction和Implementor即可。
2) 提高可扩充性
² 可以独立地对Abstraction和Implementor层次结构进行扩充。
3) 实现细节对客户透明
² 可以对客户隐藏实现细节,例如共享Implementor对象以及相应的引用计数机制(如果有的话)
Ø 实现(使用Bridge模式时需注意的一些问题)
1) 仅有一个Implementor(前面仿函数的例子就属于这种情况)
² 在仅有一个实现的时候,没有必要创建一个抽象的Implementor类。这是Bridge模式的退化情况:在Abstraction与Implementor之间有一种一对一的关系。尽管如此,当你希望改变一个类的实现不会影响已有的客户程序时,模式的分离机制还是非常有用的,也就是说,不必重新编译它们,仅需重新链接即可。
² 创建正确的Implementor对象。当存在多个Implementor类的时候,你应该用何种方法在何时何处确定创建哪一个Implementor类呢?
1) 方法一:如果Abstraction知道所有的ConcreteImplementor类,它就可以在它的构造器中对其中一个类进行实例化,它可以通过传递给构造器的参数确定实例化哪一个类。
2) 方法二:选择缺省实现,然后根据需要改变这个实现。
3) 方法三:代理给一个对象,由它一次决定。譬如引入一个factory对象,由它来创建正确的Implementor类对象。
² 共享Implementor对象
如何利用C++中常用的Handle/Body方法在多个对象间共享一些实现:其中Body有一个对象引用计数器,Handle对它进行增减操作。将共享程序体付给句柄的代码一般具有如下形式:
Handle& Handle::operator=(const Handle& other)
{
Other._body->Ref();
_body->Unref();
If(_body->RefCount() == 0)
{
Delelte _body;
}
_body = other._body;
Return *this;
}
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/17014649/viewspace-611686/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/17014649/viewspace-611686/