设计模式之装饰者模式

定义

动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

设计原则

类应该对扩展开发,对修改关闭。

第一次设计

现在我们有一家饮料店,下面是它最开始的设计。
Beverage是一个抽象类,店内所有提供的饮料都要继承自此类。并且每个子类都要实现cost()来返回饮料的价格。

 

很明显,这样使得维护变成了一个噩梦。

第二次设计

我们从它的基类入手,给每一个调料代表一个bool值,cost()不再是一个抽象方法,我们在基类中提供它的实现,然后根据调料的布尔值计算它的价格。
在子类中,覆盖过的cost()会扩展超类的功能,把指定的饮料类型的价钱也加进来(同时加上父类cost()的总价)。
下面让我们看看这样设计的问题:
  • 调料价格改变需要更改现有代码
  • 出现新的调料,需要加上新的方法和修改cost()方法。
  • 如果顾客需要双份调料怎么办

开放-关闭原则

设计原则:类应该对扩展开放,对修改关闭。
我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。这样设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

第三次设计

下面开始我们的设计:
我们以饮料为主体,然后在运行时以调料来装饰饮料。
现在我们需要一杯摩卡和奶泡深焙咖啡。

1.拿一个深焙咖啡(DarkRoast)对象

以DarkRoast对象开始。DarkRoast继承自Beverage,并且有一个计算饮料价钱的cost()方法。

 

 2.以摩卡(Mocha)对象装饰它

 

建立一个Mocha对象,并用它将DarkRoast对象包起来。
Mocha对象是一个装饰者,它的类型反映了它所装饰的对象(这里就是Bevarage)。
反映:就是两个类型一致。
Mocha也有一个cost()方法。通过多态,也可以把Mocha所包裹的任何Bevarage当成Bevarage。

3.以奶泡(Whip)对象装饰它

建立一个Whip装饰者,并用它将Mocha对象包起来。DarkRoast继承自Bevarage,且有一个cost()方法,用来计算饮料价格。
Whip是一个装饰者,它也反映了DarkRoast类型,并包括cost()方法。

被Mocha和Whip包起来的DarkRoast对象仍然是一个Bevarage,仍然可以具有DarkRoast的一切行为,包括调用它的cost()方法。

4.调用cost()方法,并依赖委托将调料的价钱加上去

 现在到了算钱的时候。通过调用最外围装饰者(Whip)的cost()就可以了。Whip对象的cost()会先委托它的装饰的对象(Mocha)计算出价钱,然后再加上奶泡的价钱。

定义装饰者模式

 动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

实现

类图

Beverage

public abstract class Beverage//抽象类
{
public string description = "Unknow Beverage";
public virtual string getDescription()//已经实现
{
return description;
}
public abstract double cost();//必须在子类中实现
}

调料抽象类

public abstract class ComdimentDecorator : Beverage////抽象装饰者
{
public abstract string getDescription();//所有的调料都必须重新实现此方法
}

饮料代码

public class Espresso : Beverage//让Espresso扩展自Beverage,因为Espresso是一种饮料
{
public override string getDescription()//设置饮料的描述
{
return "Espresso";
}
public override double cost()//计算Espresso价格,现在不管调料价格
{
return 1.99;
}
}
public class HouseBlend : Beverage//具体组件
{
public override string getDescription()
{
return "House Blend";
}
public override double cost()
{
return 0.89;
}
}

调料代码

public class Mocha : ComdimentDecorator//装饰者
{
private Beverage beverage;//用一个实例变量记录饮料,也就是被装饰者
public Mocha(Beverage beverage)//把饮料当作构造器的参数,在由构造器将此饮料记录在实例变量中
{
this.beverage = beverage;
}
public override double cost()
{
return 0.2 + beverage.cost();
}
public override string getDescription()
{
return beverage.getDescription() + ",Moche";
}
}

测试

static void Main(string[] args)
{
Beverage beverage = new Espresso();
beverage = new Mocha(beverage);
Console.WriteLine(beverage.getDescription() + "$" + beverage.cost());

Beverage beverage2 = new Espresso();//制造一个Espresso对象
beverage2 = new Mocha(beverage2);//用Mocha装饰它
beverage2 = new Mocha(beverage2);//用第二杯Mocha装饰它
beverage2 = new Whip(beverage2);//用Whip装饰它
Console.WriteLine(beverage2.getDescription() + "$" + beverage2.cost());

Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
beverage3.getDescription();
Console.WriteLine(beverage3.getDescription() + "$" + beverage3.cost());
Console.ReadLine();
}

 

posted on 2018-01-08 10:17  Mr.Tan&  阅读(655)  评论(0编辑  收藏  举报

导航