代理模式和装饰模式总是让人混淆,他们形式上确实非常像。
代理模式
结构:
实现:
有这样一种场景,一种特别庞大的组件需要在特定的时候被绘制出来,这种庞大的组件绘制了之后会造成系统的负担。
此时可以使用代理模式,在确定这个组件必须被绘制的时候,才通过代理,将这个组件绘制出来。
class Component//普通组件
{
public:
virtual void Draw();
};
class HugeComponent:public Component//特大组件
{
public:
virtual void Draw();
};
class Proxy :public Component//代理
{
public:
Proxy(Component*);//代理组件
//将对组件的绘制转接到维护的组件对象
virtual void Draw() { _componet->Draw(); };
private:
Component* _componet;
};
装饰模式
结构:
实现:
有这样一种场景,一种组件需要被绘制出来,但是这种组件可能加上各种不同的元素,比如加个边框。
此时可以使用装饰者模式,用不同的装饰者类装饰这个组件后,再通过这些装饰者类的绘制就可以在绘制这个组件的基础上加上边框。
class Component//普通组件
{
public:
Component();
virtual void Draw();
};
class Decorator :public Component//装饰者类
{
public:
//装饰者一定要有被装饰对象
Decorator(Component* c) { _component = c; };
//将命令转交给被装饰对象
virtual void Draw() { _component->Draw(); };
private:
Component* _component;
};
class BorderDecorator :public Decorator//带边框装饰者
{
public:
BorderDecorator(Component* c,float borderWidth) ;
//不只是绘图,还要绘边框
virtual void Draw() { Decorator::Draw(); DrawBorder(_border); };
private:
void DrawBorder();//加边框
float _border;
};
void client(Component* component)
{
component->Draw();
}
void main()
{
Component * com = new Component();//一个普通的组件
//用一个带边框的装饰者子类装饰
BorderDecorator *bd = new BorderDecorator(com,1.0f);
client(bd);//客户调用带边框的装饰者
}
差别
代理模式和装饰模式实在太像了,如果可以的话,代码都能写成一样,从形式上来区分是很难的。
但代理和装饰的实现目的是不一样的。
代理模式是将被代理类交给代理来管理,所以每一个代理维护一个被装饰对象。
装饰模式是用装饰类的基类维护一个对象,用装饰类的子类实现装饰,这就意味着,被装饰类并不受装饰者制,装饰者只能决定是自己是不是要装饰这个被装饰类,而无法决定被装饰者的行为。
这是他们最大的区别。
所以装饰模式如果没有装饰类的子类实现或者子类实现像代理一样,就蜕化为代理模式了
优点
装饰者模式有两个主要优点:
1.比多重继承灵活,装饰者类由于基类维护的是一个被装饰类基类,所以可以动态置换。
2.避免暴露被装饰者调用细节,装饰者子类只需要决定要不要为被装饰者添加装饰,而不能对被装饰者进行任何修改。
代理模式的优点主要有:
1.和装饰者一样,比多重继承灵活。
2.由于代理维护了一个对象引用,因此代理对这个对象拥有控制权,代理可以决定对象执行什么,不执行什么,这是与装饰者的不同。同时也是比装饰者灵活的地方。
思考
比起用装饰者或者代理这样的设计模式,实现同样的效果为什么不用回调呢?比如在代理的函数中传入函数指针,使用回调,不是比继承更方便吗?
事实上传函数指针这种方式确实更灵活,但不见得方便,本例中,只有一个函数实现了代理,如果有10个呢?难道要传10个指针?
但回调的优势也很明显,就是灵活,如以随意组合代理是其一,不用在代理内部维护一个引用是其二。
使用看习惯吧。没有特别需求的情况下使用代理这样的会更合适一些,因为指针类型自己维护不见得能保证正确,BUG会隐藏深一点儿。