设计模式实践之《一》:装饰器模式
使用场合
当需要给已经存在的一个类增加新的功能,同时又要保持接口不变,从目前的来看主要有以下方式:
1、通过继承,扩展原有类的功能,这是最简单的方式,但是如果又有新的功能加入,难道又要在此基
础上继承,显然这样会产生过多的继承关系
2、通过代理,如果使用代理模式也可以,代理模式中代理类与被代理类具有相同的接口,同时也可以在
代理类中扩充被代理类的功能,特别是使用动态代理效果更好,AOP就是使用动态代理来扩充被代理
类某些方面的功能,如日志功能,权限功能(如果希望了解动态代理,参见Castle 动态代理介绍、
如何使用动态代理实现权限验证 )。当然,这样虽好,但在扩充新的功能的时候也是要么修改该类,
要么继承该类,也有上面的弊病。
3、通过装饰器,如果使用装饰器,我们一方面保证了和被装饰对象的接口不变,同时也能很方便地扩充
新功能,如果在有新的功能我们只需要针对新功能新建一个装饰器就可以了,多个装饰器可以根据不
同的需要,组合使用,这样就实现了灵活的扩充方式。
下面我们来看一个UML图:
原始类为被装饰对象,装饰器一方面要继承原始类,需要拥有和它一样的接口,同时又具有原始类的引
用,这样可以对任何继承自原始类的类型进行装饰,比如:原始类在经过装饰器A的装饰后,具有了装饰
器A所赋予的特殊功能,同时装饰器A又可以被装饰器B装饰,让原始类具有装饰器A、B的双重附加功能
以次类推,可以实现装饰器的自由组合。在WEB中的外观显示方面,使用不同的装饰器则可以定义不同
的外观。
同时根据面向对象中的职责单一的原则,我们可以定义出一系列职责单一的装饰器,增强系统灵活性。
使用案例:
下面我将通过一个简单的WinForm程序向大家展示装饰器模式所显示出来的魅力。
类图如下:
Machine将是需要被装饰的对象,AdvMachine是其继承的子类(本例中不打算实现它,只是为了图解直
观),MachineDecoraterBase是所有装饰器的基类,在它之下有三个面向不同方面的装饰器,分别是
LogDecorater(日志装饰器)、(NotifyDecorater)通知装饰器、(TransactionDecorater)事务装饰器。我将分别将
3个装饰器进行不同的组合,产生不同的装饰效果。
先来看看界面
通过一个按钮触发了Machine的Work方法,下面的复选框是表示如何对Machine进行装饰,选择的装饰器将应用于
machine上。下面的执行记录则显示是使用了装饰器后的效果。
Machine类的定义:
1
/********************
2
* 作者:米小波
3
* 日期:2005-12-19
4
* *****************/
5
using System;
6
using System.Collections.Generic;
7
using System.Text;
8
9
namespace DecoratePatternDemo
10
{
11
class Machine
12
{
13
14
/// <summary>
15
/// 该类唯一的方法,注意使用virtual,方便自类重载
16
/// </summary>
17
public virtual string Work()
18
{
19
return "Machine is doing something !\r\n";
20
}
21
}
22
}
23
/********************2
* 作者:米小波3
* 日期:2005-12-19 4
* *****************/5
using System;6
using System.Collections.Generic;7
using System.Text;8

9
namespace DecoratePatternDemo10
{11
class Machine12
{13

14
/// <summary>15
/// 该类唯一的方法,注意使用virtual,方便自类重载16
/// </summary>17
public virtual string Work()18
{19
return "Machine is doing something !\r\n";20
}21
} 22
}23

其中有一个简单的不能再简单的Work方法,返回一个字符串,这个方法将在后面被装饰。
MachineDecoraterBase类的定义:
1
/********************
2
* 作者:米小波
3
* 日期:2005-12-19
4
* *****************/
5
using System;
6
using System.Collections.Generic;
7
using System.Text;
8
9
namespace DecoratePatternDemo
10
{
11
class MachineDecoraterBase:Machine
12
{
13
protected Machine m_decoratedMachine = null;
14
15
/// <summary>
16
/// 被修饰对象
17
/// </summary>
18
public Machine DecoratedMachine
19
{
20
get { return this.m_decoratedMachine;}
21
}
22
23
/// <summary>
24
/// 构造函数
25
/// </summary>
26
/// <param name="decoratedMachine">被修饰对象</param>
27
public MachineDecoraterBase(Machine decoratedMachine)
28
{
29
this.m_decoratedMachine = decoratedMachine;
30
}
31
32
public override string Work()
33
{
34
return base.Work();
35
}
36
}
37
}
38
该类一方面实现了被装饰对象的接口,一方面可以通过容纳一个也同样实现了被修饰对象的接口的对
/********************2
* 作者:米小波3
* 日期:2005-12-19 4
* *****************/5
using System;6
using System.Collections.Generic;7
using System.Text;8

9
namespace DecoratePatternDemo10
{11
class MachineDecoraterBase:Machine12
{13
protected Machine m_decoratedMachine = null;14

15
/// <summary>16
/// 被修饰对象17
/// </summary>18
public Machine DecoratedMachine19
{20
get { return this.m_decoratedMachine;}21
}22

23
/// <summary>24
/// 构造函数25
/// </summary>26
/// <param name="decoratedMachine">被修饰对象</param>27
public MachineDecoraterBase(Machine decoratedMachine)28
{29
this.m_decoratedMachine = decoratedMachine;30
}31

32
public override string Work()33
{34
return base.Work();35
}36
}37
}38

象,该对象将在装饰器中被装饰。
接下来是LogDecorater:
1
/********************
2
* 作者:米小波
3
* 日期:2005-12-19
4
* *****************/
5
using System;
6
using System.Collections.Generic;
7
using System.Text;
8
9
namespace DecoratePatternDemo
10
{
11
class LogDecorater:MachineDecoraterBase
12
{
13
public LogDecorater(Machine decoratedMachine):base(decoratedMachine)
14
{}
15
16
17
/// <summary>
18
/// 重载方法,增加日志操作
19
/// </summary>
20
/// <returns>装饰后的结果</returns>
21
public override string Work()
22
{
23
string log = "This operation is logged :" + System.DateTime.Now.ToShortDateString()+"\r\n";
24
string originalString = this.DecoratedMachine.Work();
25
26
return originalString + log;
27
}
28
}
29
30
}
31
该类进行了日志装饰。
/********************2
* 作者:米小波3
* 日期:2005-12-19 4
* *****************/5
using System;6
using System.Collections.Generic;7
using System.Text;8

9
namespace DecoratePatternDemo10
{11
class LogDecorater:MachineDecoraterBase12
{13
public LogDecorater(Machine decoratedMachine):base(decoratedMachine)14
{}15

16

17
/// <summary>18
/// 重载方法,增加日志操作19
/// </summary>20
/// <returns>装饰后的结果</returns>21
public override string Work()22
{23
string log = "This operation is logged :" + System.DateTime.Now.ToShortDateString()+"\r\n";24
string originalString = this.DecoratedMachine.Work();25

26
return originalString + log;27
}28
}29

30
}31

NotifyDecorater装饰类:
1
/********************
2
* 作者:米小波
3
* 日期:2005-12-19
4
* *****************/
5
using System;
6
using System.Collections.Generic;
7
using System.Text;
8
9
namespace DecoratePatternDemo
10
{
11
class NotifyDecorater : MachineDecoraterBase
12
{
13
public NotifyDecorater(Machine decoratedMachine) : base(decoratedMachine)
14
{}
15
16
17
/// <summary>
18
/// 重载方法,增加通知操作
19
/// </summary>
20
/// <returns>装饰后的结果</returns>
21
public override string Work()
22
{
23
string notify = "This notify is sended :" + System.DateTime.Now.ToShortDateString() + "\r\n";
24
string originalString = this.DecoratedMachine.Work();
25
26
return originalString + notify;
27
}
28
}
29
}
30
/********************2
* 作者:米小波3
* 日期:2005-12-19 4
* *****************/5
using System;6
using System.Collections.Generic;7
using System.Text;8

9
namespace DecoratePatternDemo10
{11
class NotifyDecorater : MachineDecoraterBase12
{13
public NotifyDecorater(Machine decoratedMachine) : base(decoratedMachine) 14
{}15

16

17
/// <summary>18
/// 重载方法,增加通知操作19
/// </summary>20
/// <returns>装饰后的结果</returns>21
public override string Work()22
{23
string notify = "This notify is sended :" + System.DateTime.Now.ToShortDateString() + "\r\n";24
string originalString = this.DecoratedMachine.Work();25

26
return originalString + notify;27
}28
}29
}30

TransactionDecorater装饰类:
1
/********************
2
* 作者:米小波
3
* 日期:2005-12-19
4
* *****************/
5
using System;
6
using System.Collections.Generic;
7
using System.Text;
8
9
namespace DecoratePatternDemo
10
{
11
class TransactionDecorater:MachineDecoraterBase
12
{
13
public TransactionDecorater(Machine decoratedMachine) : base(decoratedMachine)
14
{}
15
16
/// <summary>
17
/// 重载方法,增加事务处理
18
/// </summary>
19
/// <returns>装饰后的结果</returns>
20
public override string Work()
21
{
22
string transBegin = "Transaction Begin :" + System.DateTime.Now.ToShortDateString() + "\r\n";
23
string originalString = this.DecoratedMachine.Work();
24
string transEnd = "Transaction End :" + System.DateTime.Now.ToShortDateString() + "\r\n";
25
26
return transBegin + originalString + transEnd;
27
}
28
}
29
}
30
/********************2
* 作者:米小波3
* 日期:2005-12-19 4
* *****************/5
using System;6
using System.Collections.Generic;7
using System.Text;8

9
namespace DecoratePatternDemo10
{11
class TransactionDecorater:MachineDecoraterBase12
{13
public TransactionDecorater(Machine decoratedMachine) : base(decoratedMachine) 14
{}15

16
/// <summary>17
/// 重载方法,增加事务处理18
/// </summary>19
/// <returns>装饰后的结果</returns>20
public override string Work()21
{22
string transBegin = "Transaction Begin :" + System.DateTime.Now.ToShortDateString() + "\r\n";23
string originalString = this.DecoratedMachine.Work();24
string transEnd = "Transaction End :" + System.DateTime.Now.ToShortDateString() + "\r\n";25

26
return transBegin + originalString + transEnd;27
}28
}29
}30

我们在客户端对以上类进行调用:
1
/// <summary>
2
/// 执行Machine方法
3
/// </summary>
4
/// <param name="sender"></param>
5
/// <param name="e"></param>
6
private void btnExecMethod_Click(object sender, EventArgs e)
7
{
8
this.txtExecHistory.Text = GetMachine().Work();
9
}
10
11
/// <summary>
12
/// 根据复选框的选择给machine相应的装饰,返回最终装饰器
13
/// </summary>
14
/// <returns></returns>
15
private Machine GetMachine()
16
{
17
Machine mac = new Machine();
18
Machine finallyMac = mac;
19
if (chkLogDecorater.Checked)
20
{
21
finallyMac = new LogDecorater(finallyMac);
22
}
23
if (chkNotifyDecorater.Checked)
24
{
25
finallyMac = new NotifyDecorater(finallyMac);
26
}
27
if (chkTransactionDecorater.Checked)
28
{
29
finallyMac = new TransactionDecorater(finallyMac);
30
}
31
return finallyMac;
32
}
可见,客户的调用接口是没有变的(还是使用Machine),但是显示内容却改变了,如下图几个不同的
/// <summary>2
/// 执行Machine方法3
/// </summary>4
/// <param name="sender"></param>5
/// <param name="e"></param>6
private void btnExecMethod_Click(object sender, EventArgs e)7
{8
this.txtExecHistory.Text = GetMachine().Work();9
}10

11
/// <summary>12
/// 根据复选框的选择给machine相应的装饰,返回最终装饰器13
/// </summary>14
/// <returns></returns>15
private Machine GetMachine()16
{17
Machine mac = new Machine();18
Machine finallyMac = mac;19
if (chkLogDecorater.Checked)20
{21
finallyMac = new LogDecorater(finallyMac); 22
}23
if (chkNotifyDecorater.Checked)24
{25
finallyMac = new NotifyDecorater(finallyMac);26
}27
if (chkTransactionDecorater.Checked)28
{29
finallyMac = new TransactionDecorater(finallyMac);30
}31
return finallyMac;32
}装饰组合
(不好意思,写到这里才发现界面上我把事务写成事物了,我也懒的再截图,将就凑合吧)
学过AOP的人看了可能会有似曾相识的感觉,的确这个例子使用动态代理来实现也能达到同样的效
果,动态代理可以通过拦截方法调用来为一个对象织入某个方面的额外的功能。可见动态代理能实现的
某些功能,使用装饰器也能实现。
本示例完整的程序代码可以到https://files.cnblogs.com/mixiaobo/DecoratePatternDemo.rar下载




浙公网安备 33010602011771号