Bridge模式与仿函数

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接口并定义它的具体实现。

Ø         协作:Abstractionclient的请求转发给它的Implementor对象。

Ø         效果(优点):

1)分离接口及其实现部分

²        一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。

²        AbstractionImplementor分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类时,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间二进制兼容性,一定要有这个性质。

²        接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道AbstractionImplementor即可。

2)    提高可扩充性

²        可以独立地对AbstractionImplementor层次结构进行扩充。

3)    实现细节对客户透明

²        可以对客户隐藏实现细节,例如共享Implementor对象以及相应的引用计数机制(如果有的话)

Ø         实现(使用Bridge模式时需注意的一些问题)

1)  仅有一个Implementor(前面仿函数的例子就属于这种情况)

²        在仅有一个实现的时候,没有必要创建一个抽象的Implementor类。这是Bridge模式的退化情况:在AbstractionImplementor之间有一种一对一的关系。尽管如此,当你希望改变一个类的实现不会影响已有的客户程序时,模式的分离机制还是非常有用的,也就是说,不必重新编译它们,仅需重新链接即可。

²        创建正确的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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值