您的位置:首页 > 其它

《Head First Design Patterns》笔记三:装饰者模式(Decorator Pattern)

2009-04-14 15:34 537 查看
前言

什么是装饰呢,通俗来说,人靠衣装,佛靠金装,这里呢,人就是被装饰者,衣服就是装饰品,你穿再多衣服,性质没变,还是个人啦,你还是可以再戴上新戒指,而不必关心这人之前穿了什么衣服.

引申到软件工程里呢,就是一个已存在的类,可能需要不断的增加新功能,那么我们新建新功能 当做装饰品,装饰现有的类,而不必要去不断修改已有的代码,要知道碰旧代码通常是一件让人头疼的事情.这符合设计模式的一个原则:对扩展开放,而对修改关闭.就是说有新功能我们尽量在外部扩展用新代码,而不是去内部修改旧的代码,把新功能加进去.ok,闲话少说,言归正传,我们这里以一个咖啡店作为例子来讲解这个装饰者模式.

需求

新业务上门了,现在我们需要为一家咖啡店做买单系统,咖啡店提供的咖啡有espresso(浓缩咖啡) ,houseblend(家常咖啡),配料有 peppermint syrup(薄荷汁),milk(牛奶),soy(豆浆),mocha(摩卡,就是里面加巧克力),whipped milk(奶泡,会浮在上面的那种热牛奶,泡沫状),1种咖啡,加任意1种或多种配料就可以组合成几十上百种咖啡了

初步设计

我们当然可以去玩排列组合的把戏,把所有能组成的咖啡都去做1个类,那就是个类炸弹,把我们会炸晕的. so我们考虑把配料作为成员,放在咖啡类里面.设计如图



参考以上uml图表,有加牛奶的话,就设置hasMilk为true.加豆浆的话就设置hasSoy为true,这样就很容易实现需求,但是问题来了,一旦有了新的配料呢, 那么我们就不得的不修改coffee类了,如果用户不只要1份milk,而是要double呢,这些后期维护问题使得我们不断的修改原有的代码,这破坏了类的封装,违反了我们的设计原则,尽量对扩展开放,而不是对修改开放.

进一步设计

我们考虑装饰者模式,客户可以在运行时动态的增添功能,而不是在编译的时候去增添功能.这样的设计十分灵活.如下图.



再回头考虑咖啡店,把咖啡作为ConcreteComponent,把调料作为DecoratorComponent,咖啡加入任何调料,还是一杯咖啡,继续增添新调料的话,不需要关心前面已经加入了哪种调料,有新的调料加入时,只需要增加一个新的调料的类,其他代码不需要做任何修改.uml作图如下



代码

剩下的就是代码实现了, 这里把代码分为9个类,

首先是3个类,IBerverage接口,Espresso类,HouseBlend类,如下:

//file 1:Iberverage.cs
using System;
public interface IBeverage
{
string Description{get;}
double Cost();
}
//file 2:Espresso.cs
using System;
public class Espresso:IBeverage
{
public string Description
{
get{return "Espresso";}
}
public double Cost()
{
return 10;
}
}
//file 3:HouseBlend.cs
using System;
public class HouseBlend:IBeverage
{
public string Description
{
get{return "HouseBlend";}
}
public double Cost()
{
return 8;
}
}


然后是扮演装饰角色的5个类,包括IDecoratorBeverage虚类,Milk类,Mint类,Soy类,Whip类

如下:

//file 1 DecoratorBeverage.cs
using System;
public abstract class DecoratorBeverage:IBeverage
{
public abstract string Description{get;}
public abstract double Cost();
}
//file 2 Milk.cs
using System;
public class Milk:DecoratorBeverage
{
private IBeverage beverage;
public override string Description
{
get{return beverage.Description+" Milk";}
}
public Milk(IBeverage beverage)
{
this.beverage=beverage;
}
public override double Cost()
{
return beverage.Cost()+2;
}
}
//file 3 Mint.cs
using System;
public class Mint:DecoratorBeverage
{
private IBeverage beverage;
public override string Description
{
get{return beverage.Description+" Mint";}
}
public Mint(IBeverage beverage)
{
this.beverage=beverage;
}
public override double Cost()
{
return beverage.Cost()+2.5;
}
}
//file 4 Soy.cs
using System;
public class Soy:DecoratorBeverage
{
private IBeverage beverage;
public override string Description
{
get{return beverage.Description+" Soy";}
}
public Soy(IBeverage beverage)
{
this.beverage=beverage;
}
public override double Cost()
{
return beverage.Cost()+1;
}
}
//file 5 Whip.cs
using System;
public class Whip:DecoratorBeverage
{
private IBeverage beverage;
public override string Description
{
get{return beverage.Description+" Whip";}
}
public Whip(IBeverage beverage)
{
this.beverage=beverage;
}
public override double Cost()
{
return beverage.Cost()+1.5;
}
}


最后就是测试运行代码了 Program.cs 如下:

using System;
public class Program
{
public static void Main()
{
IBeverage beverage1=new Espresso();
IBeverage beverage2=new Whip(new Soy(new Espresso()));
IBeverage beverage3=new Milk(new Milk(new Mint(new HouseBlend())));
Console.WriteLine(beverage1.Description+":$"+beverage1.Cost());
Console.WriteLine(beverage2.Description+":$"+beverage2.Cost());
Console.WriteLine(beverage3.Description+":$"+beverage3.Cost());
}
}


运行结果:

Espresso:$10
Espresso Soy Whip:$12.5
HouseBlend Mint Milk Milk:$14.5

总结

装饰者模式就是可以动态的将组件添加到已有的类中,以实现新功能。我们在实际编程中遇见需求更改,添加新功能的时候经常用到这个模式,这也符合面向对象的一个设计原则:尽量使用组合,而不是继承

下一篇:《Head First Design Patterns》笔记四:工厂模式(Factory Pattern)

上一篇:《Head First Design Patterns》笔记二:观察者模式(Observer Pattern)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: