您的位置:首页 > 其它

【设计模式】装饰模式

2016-07-17 23:11 155 查看
能力要求:抽象能力 + 业务知识

设计要结合具体的业务,如果业务知识不够,常常容易出现设计过度的现象——那些不常变的地方,简单即可。事实上,很多良好的设计都是迭代而来的,设计并非一成不变,如果从一开始就想设计好,很容易陷入过度设计。

Q:设计一个类,其职责是将数据输出到文件。

Class Iout

{

Public:

int outToFile();

……

};

如果想增加一个输出到网络的功能的呢?

再增加一个函数?——不符合开闭原则

输出操作作为一个基类,然后具体输出到哪里,由子类去实现?——可行

如果需要压缩后再输出呢?如果还需要加密再输出呢?如果需要先加密再压缩再输出呢?

……

显然,我们可以通过继承的方式来给原对象增加新功能,但是面对那么多的组合方式,将产生大量的子类!

此时可以选择使用装饰者模式,动态地给别的对象增加额外的职责,它采用组合的方式比生成子类更加灵活。

类图:



各个角色介绍:

抽象构件(Component):给出一个抽象接口,以规范准备接收附加责任的对象。

具体构件(Concrete Component):定义一个将要接收附加责任的类。

装饰(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

具体装饰(Concrete Decorator):负责给构件对象”贴上”附加的责任。

实现的例子:

//抽象类Iout
class Iout
{
public:
virtual void out() = 0;

public:
virtual ~Iout()
{
cout<<"In the destructor of Iout"<<endl;
}
};

//具体类 FileOut
class FileOut: public Iout
{
public:
void out()
{
cout<<"Out to file."<<endl;
}

public:
virtual ~FileOut()
{
cout<<"In the destructor of FileOut"<<endl;
}
};

//具体类NetOut
class NetOut:public Iout
{
public:
void out()
{
cout<<"Out to network."<<endl;
}
public:
virtual ~NetOut()
{
cout<<"In the destructor of NetOut"<<endl;
}
};

//抽象类,Decorator
class Decorator:public Iout
{
protected:
Iout* myOut;
public:
Decorator(Iout* myOut):myOut(myOut) {}  //具体的输出的装饰类
virtual ~Decorator()
{
cout<<"In the destructor of Decorator"<<endl;
}
public:
void out()
{
myOut->out();
}
};

//压缩装饰类
class CompressDecorator: public Decorator
{
public:
CompressDecorator(Iout* myOut):Decorator(myOut) {}
virtual ~CompressDecorator()
{
cout<<"in the destructor of CompressDecorator"<<endl;
}
public:
void compress()
{
cout<<"Do compress."<<endl;
}
void out()
{
compress();
myOut->out();
}
};

//加密装饰类
class EncryptDecorator:public Decorator
{
public:
EncryptDecorator(Iout* myOut):Decorator(myOut) {}
~EncryptDecorator()
{
cout<<"in the destructor of EncryptDecorator"<<endl;
}
public:
void encrypt()
{
cout<<"Do encrypt."<<endl;
}
public:
void out()
{
encrypt();
myOut->out();
}
};

int main(int argc, char **argv)
{
//给FileOut增加压缩功能
Iout* fileOut(new FileOut);
Iout* compressdFileOut(new CompressDecorator(fileOut));

compressdFileOut->out();
cout<<endl;
cout<<endl<<"---------------"<<endl;

//给NetOut增加加密、压缩功能
Iout* netOut(new NetOut);
//增加加密
Iout* encryptdNetOut(new EncryptDecorator(netOut));
//增加压缩
Iout* compressdAndEncryptdFileOut(new CompressDecorator(encryptdNetOut));

compressdAndEncryptdFileOut->out();
cout<<endl;
cout<<endl<<"--------------"<<endl;

return 0;
}


适用场景:

需要扩展一个类的功能,或给一个类增加附加责任。

需要动态地给一个对象增加功能,这些功能可以再动态地撤销。

需要增加由一些基本功能的排列组合而产生的非常大量的功能。

优点:

Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。

通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

缺点:

更灵活的同时也意味着更多的复杂性。

装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。

装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。

进阶:半透明装饰模式

一般情况下,纯粹的装饰模式很少用到,大部分情况下用到的都是半透明的装饰模式。

在透明装饰模式中,要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。对于客户端而言,具体构件对象和具体装饰对象没有任何区别。

也就是应该使用如下代码:

Component  c, c1;   //使用抽象构件类型定义对象
c = new ConcreteComponent();
c1 = new ConcreteDecorator (c);


而不应该使用如下代码:

ConcreteComponent c;    //使用具体构件类型定义对象
c = new ConcreteComponent();




ConcreteDecorator c1;    //使用具体装饰类型定义对象
c1 = new ConcreteDecorator(c);


透明装饰模式可以让客户端透明地使用装饰之前的对象和装饰之后的对象(我们前面的例子中使用的都是Iout),无须关心它们的区别,此外,还可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象。

有些时候,我们为了能够调用到新增的方法,我们不得不用具体装饰类型来定义装饰之后的对象,而具体构件类型还是可以使用抽象构件类型来定义,这种装饰模式即为半透明装饰模式,也就是说,对于客户端而言,具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的。

比如在前面的例子中,我们想要单独使用新增的压缩功能(而不输出),客户端代码片段如下所示:

……
Iout* fileOut(new FileOut);         //使用抽象构件类型定义
CompressDecorator * compressdFileOut(new CompressDecorator(fileOut));   //使用具体装饰类型定义
compressdFileOut-> compress();  //可以单独使用新接口
……


其他:

1. 如果只有一个ConcreteComponent类而没有抽象的Component类(接口),那么Decorator类经常可以是ConcreteComponent的一个子类;

2. 如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: