背景 本文讨论装饰器模式,这个模式是因为很多情况下需要动态的给对象添加功能.比如我们创建了一个Stream类.后来需要对这个数据流类动态的添加一个加密功能.有人可能说把加密方法写到流类里面啊.然后使用一个bool变量来控制开关就行了.但是这样.这个加密方法只能写一种..如果用派生类来实现.那么..对于不同的加密方法.,都要创建一个子类,举个例子.比如有时候是一些函数的组合.我们最终的派生类的数目基本上就和排列组合的数目一样了.
我们使用装饰器模式来解决这个问题.GoF描述为 “Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.”
首先看一下图.理解一下这个模式中每一个类的作用
• Component:定义了可以动态添加功能的具体类ConcreteComponents的接口.
• ConcreteComponent: 可以动态添加功能的具体类
• Decorator: 定义了动态添加到ConcreteComponent类中的功能的接口
• ConcreteDecorator: 可以添加到 ConcreteComponent.中的具体功能类.
使用代码
我们开一个面包店的例子.面包店卖蛋糕和甜点.客户可以买蛋糕和甜点,同时添加一些额外的东西.额外的东西包括奶油(Cream),樱桃(Cherry),香料(Scent)和会员(Name Card)
如果我们用派生类来实现..那么我们会有如下的类 • CakeOnly • CakeWithCreamAndCherry • CakeWithCreamAndCherryAndScent • CakeWithCreamAndCherryAndScentAndNameCard • CakeWithCherryOnly • PastryOnly • PastryWithCreamAndCherry • PastryWithCreamAndCherryAndScent • PastryWithCreamAndCherryAndScentAndNameCard • PastryWithCherryOnly • 等等等等
这简直就是噩梦..我们用装饰器模式来实现把. 首先定义Component 接口
public abstract class BakeryComponent
{
public abstract string GetName();
public abstract double GetPrice();
}
前面说过了.这个类定义了能够动态添加功能的具体类(ConcreteComponents)的接口,好吧.然后来创建具体类ConcreteComponents
class CakeBase : BakeryComponent
{
// 真实世界里,这个值应该来自数据库等
private string m_Name = "Cake Base";
private double m_Price = 200.0;
public override string GetName()
{
return m_Name;
}
public override double GetPrice()
{
return m_Price;
}
}
class PastryBase : BakeryComponent
{
//真实世界里,这个值应该来自数据库等
private string m_Name = "Pastry Base";
private double m_Price = 20.0;
public override string GetName()
{
return m_Name;
}
public override double GetPrice()
{
return m_Price;
}
}
现在基对象准备好了.看看那些可以被动态添加的功能.我们看看Decorator 类
public abstract class Decorator : BakeryComponent
{
BakeryComponent m_BaseComponent = null;
protected string m_Name = "Undefined Decorator";
protected double m_Price = 0.0;
protected Decorator(BakeryComponent baseComponent)
{
m_BaseComponent = baseComponent;
}
#region BakeryComponent Members
string BakeryComponent.GetName()
{
return string.Format("{0}, {1}", m_BaseComponent.GetName(), m_Name);
}
double BakeryComponent.GetPrice()
{
return m_Price + m_BaseComponent.GetPrice();
}
#endregion
}
注意两个地方.第一个就是类实现BakeryComponent 接口,原因是装饰后的蛋糕还是蛋糕,另一个是该类也持有一个BakeryComponent 对象,原因是,我们需要Cake和装饰的项目是is-a关系,但是事实上不是.通过加一个对象就可以模拟is-a关系.
一句话.我们使用继承实现了静态的is-a关系,而是用构成则是一个动态的is-a关系.
然后看看ConcreteDecorators 如何实现
class ArtificialScentDecorator : Decorator
{
public ArtificialScentDecorator(BakeryComponent baseComponent)
: base(baseComponent)
{
this.m_Name = "Artificial Scent";
this.m_Price = 3.0;
}
}
class CherryDecorator : Decorator
{
public CherryDecorator(BakeryComponent baseComponent)
: base(baseComponent)
{
this.m_Name = "Cherry";
this.m_Price = 2.0;
}
}
class CreamDecorator : Decorator
{
public CreamDecorator(BakeryComponent baseComponent)
: base(baseComponent)
{
this.m_Name = "Cream";
this.m_Price = 1.0;
}
}
然后看一下如何给一个会员卡添加一个打折的信息.
class NameCardDecorator : Decorator
{
private int m_DiscountRate = 5;
public NameCardDecorator(BakeryComponent baseComponent)
: base(baseComponent)
{
this.m_Name = "Name Card";
this.m_Price = 4.0;
}
public override string GetName()
{
return base.GetName() +
string.Format("\n(Please Collect your discount card for {0}%)",
m_DiscountRate);
}
}
现在我们的客户端可使用Decorator 来装饰ConcreteComponents 生成不同的组合.看看例子
static void Main(string[] args)
{
// 创建一个基本的蛋糕
CakeBase cBase = new CakeBase();
PrintProductDetails(cBase);
// 添加奶油
CreamDecorator creamCake = new CreamDecorator(cBase);
PrintProductDetails(creamCake);
// 添加樱桃
CherryDecorator cherryCake = new CherryDecorator(creamCake);
PrintProductDetails(cherryCake);
// 添加香料
ArtificialScentDecorator scentedCake = new ArtificialScentDecorator(cherryCake);
PrintProductDetails(scentedCake);
// 添加一张会员卡
NameCardDecorator nameCardOnCake = new NameCardDecorator(scentedCake);
PrintProductDetails(nameCardOnCake);
// 添加一个甜点
PastryBase pastry = new PastryBase();
PrintProductDetails(pastry);
// 添加 奶油和樱桃
CreamDecorator creamPastry = new CreamDecorator(pastry);
CherryDecorator cherryPastry = new CherryDecorator(creamPastry);
PrintProductDetails(cherryPastry);
}
运行效果
亮点在那里 装饰器模式是很典型的开放-封闭原则的例子.我们的类对扩展开放,而对修改封闭. Demo下载 [downloadicon href=http://pan.baidu.com/share/link?shareid=88379&uk=1493685990]DecoratorSampleApp.zip[/downloadicon]
原文地址:UnderstandingplusandplusImplementingplusDecoratorp 著作权声明:本文由http://leaver.me 翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

