前段时间忙着出去玩,又是骑行又是去岛上,搞得一度没有时间继续写下去(其实还不是自己懒哈哈哈),今天写装饰者模式,在看书实现的过程中还遇到了一些插曲,顺便也一起写下来。
1.1定义
装饰者模式:
动态的将责任附加到对象上,若要拓展功能,装饰者提供了比继承更有弹性的替代方案。
OO原则:
类应该对拓展开放,对修改关闭。
我们的目标是允许类容易拓展,在不修改现有代码的基础上,就可以搭配新的行为。这样的设计具有弹性可以应该对改变,可以接受新的功能来应对改变的需求。
2.1 需求
这是一个咖啡店咖啡的基类和其实现,现在的问题是咖啡其实是有很多不同的调料,例如蒸奶,豆浆,摩卡等等,咖啡店会根据咖啡加入的调料的不同而收取不同的费用。
ps:调料会不定时更新,添加等等。
2.2 分析
这个设计的主要问题是使用组合替代继承,避免类爆炸。
如果只是使用继承,因为多种咖啡可以添加多种调料,所以最后的结果可以有非常多个,用具体的实现来覆盖所有这些情况显然是不可行的,这时我们就要用组合替代继承。
2.3 失败的例子
书里面提到了一个应对这个问题的一个错误思路,也不能叫错误思路,只是说开放关闭原则做的不好,有需求变化时,甚至要修改超类的代码。比如新增一种调料
这种设计在应对需求变化时非常的不方便,因为继承这个东西实在是不太灵活,要用组合替代继承
3.1 具体实现
3.1.1 装饰者模式的要点
- 装饰者和被装饰者具有相同的超类型
- 你可以用一个或者多个装饰者包装一个对象
- 既然装饰者和被装饰者拥有相同的超类型,所以在任何需要原始对象的情况下,都可以用装饰过的对象代替他。
- (关键) 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特殊目的。
- 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。
3.2.1 具体代码实现
先是对原始的饮料超类做出一些修改:
public abstract class Beverage
{
protected string description = "unknow beverage";
public virtual string getDescription()
{
return description;
}
public abstract double cost();
}
这里的virtual关键字很重要,我不知道java里对方法重写是如何定义的,在C#中,方法不加virtual或者abstract,或者本身不处于接口中,这样的方法不能被重写override,只能被覆盖new,但是覆盖后的方法调用原则是,如果直接调用基类的方法,还是最原始的那个起作用,调用子类的方法,会使用new后的替代,所以现在的场景需要override不需要new.
下面是装饰者的超类:
//装饰者超类
public abstract class CondimentDectorator : Beverage
{
public override abstract string getDescription();
}
所有继承CondimentDectorator的类都必须重写getDescription()方法,而CondimentDectorator中的getDescription()其实又重写了基类的方法,所以最终是重写了基类的方法。
下面是饮料Beverage类的一些子类:
//浓咖啡
public class Espresso : Beverage
{
public Espresso()
{
description = "espresso";
}
public override double cost()
{
return 1.99;
}
}
//不知道叫什么的咖啡
public class HouseBlend : Beverage
{
public HouseBlend()
{
description = "houseblend";
}
public override double cost()
{
return 2.33;
}
}
下面是最关键的具体装饰者的代码:
//调料装饰者摩卡
public class Mocha : CondimentDectorator
{
Beverage beverage;
public Mocha(Beverage b)
{
beverage = b;
}
public override double cost()
{
return 0.2 + this.beverage.cost();
}
public override string getDescription()
{
return this.beverage.getDescription() + " mocha";
}
}
//调料装饰者奶油
public class Whip : CondimentDectorator
{
Beverage beverage;
public Whip(Beverage b)
{
beverage = b;
}
public override double cost()
{
return 0.01 + this.beverage.cost();
}
public override string getDescription()
{
return this.beverage.getDescription() + " Whip";
}
}
这里在设计中用到了C#的virtual和abstract组合来实现基类中有方法体的方法在子类中必须重写的需求。
3.1 使用示例
static void Main(string[] args)
{
Beverage b = new HouseBlend();
b = new Whip(b);
b = new Mocha(b);
Console.WriteLine(b.getDescription());
Console.WriteLine("==");
Console.WriteLine("cost:"+b.cost());
Console.ReadKey();
}
输出结果即为: