初学设计模式【3】装饰模式——Decorator

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

如定义中说的那样,装饰模式可以动态的为对象添加新的职责,通过继承也能达到扩展类功能的目的。那么这两种方式的区别在哪里呢?虽然通过继承也能达到扩展功能但这种方式是在编译时静态决定的,所有子类都会继承到相同的行为,不具有弹性,应对未来变化的能力较低。而通过Decorator模式就可以在运行时动态扩展,而且被装饰对象可以自由组合新的行为,更易于应对未来的功能扩展。

设计原则:类应该对扩展开放,对修改关闭这就是“开放关闭”原则。类应该是容易扩展的在不需要修改现有代码的情况下,这样的设计可以应对改变,而且不会引入新的bug。Decorator模式就很好的遵循了这个原则。

UML类图

  

 

  分析:ConcreteComponent代表一个被装饰对象,扩展自抽象类Component;Decorator是装饰者共同实现的接口;派生自Decorator的为具体的装饰对象;至于装饰者与被装饰者共同派生自Component是为了达到“装饰者与被装饰者”的类型匹配,因为我们用Decorator装饰了ConcreteComponent后不能改变ConcreteComponent的类型;Component不实现具体的行为,只是为了达到前面所说的装饰与被装饰者的类型匹配。

实例

  咖啡馆下单系统:顾客可以选择咖啡的类型,还可以选择加入一种或多种调料,最后系统可以根据不同的咖啡类型及所加调料计算出总金额,打印出清单。

显然,我们用调料来装饰具体咖啡,uml图如下:

截自:《HeadFirst设计模式》

Beverage

1 public abstract class Beverage {
2     String description = "Unknown Beverage";
3 
4     public String getDescription() {
5         return description;
6     }
7 
8     public abstract float cost();
9 }

Decaf

 1 public class Decaf extends Beverage {
 2 
 3     public Decaf(String description) {
 4         super.description = description;
 5     }
 6 
 7     @Override
 8     public float cost() {
 9         return 1.2f;
10     }
11 
12 }

 

Espresso

View Code
 1 public class Espresso extends Beverage {
 2 
 3     public Espresso(String descraption) {
 4         super.description = descraption;
 5     }
 6 
 7     @Override
 8     public float cost() {
 9         return 1.00f;
10     }
11 
12 }

 

CondimentDecorator

1 public abstract class CondimentDecorator extends Beverage {
2     @Override
3     public abstract String getDescription();
4 
5 }

Milk

 1 public class Milk extends CondimentDecorator {
 2 
 3     private Beverage beverage; // 代表被装饰对象
 4 
 5     public Milk(Beverage beverage) {
 6         this.beverage = beverage;
 7     }
 8 
 9     @Override
10     public String getDescription() {
11         return beverage.getDescription() + "+ Milk";
12     }
13 
14     @Override
15     public float cost() {
16         return 0.31f + beverage.cost();
17     }
18 
19 }

Mocha

View Code
 1 public class Mocha extends CondimentDecorator {
 2 
 3     private Beverage beverage; // 代表被装饰对象
 4 
 5     public Mocha(Beverage beverage) {
 6         this.beverage = beverage;
 7     }
 8 
 9     @Override
10     public String getDescription() {
11         return beverage.getDescription() + "+ Mocha";
12     }
13 
14     @Override
15     public float cost() {
16         return 0.11f + beverage.cost();
17     }
18 
19 }

 

 Soy

View Code
 1 public class Soy extends CondimentDecorator {
 2     private Beverage beverage; // 代表被装饰对象
 3 
 4     public Soy(Beverage beverage) {
 5         this.beverage = beverage;
 6     }
 7 
 8     @Override
 9     public String getDescription() {
10         return beverage.getDescription() + "+ Soy";
11     }
12 
13     @Override
14     public float cost() {
15         return 0.21f + beverage.cost();
16     }
17 
18 }

 

 测试

 1 import org.junit.Test;
 2 
 3 public class TestDecorator {
 4 
 5     @Test
 6     public void test() {
 7         Beverage beverage = new Decaf("Decaf");
 8         beverage = new Milk(beverage);
 9         beverage = new Milk(beverage);
10         beverage = new Mocha(beverage);
11         System.out.println(beverage.getDescription() + ":" + beverage.cost());
12     }
13 }

   测试结果:(双倍牛奶+摩卡+无咖啡因咖啡)

 

总结

 1)装饰模式的根类Component一般为abstract或接口类型,设置此类的目的只为了达到装饰者与被装饰者类型的匹配,不实现具体的行为,这一点很重要,可以结合上面的例子仔细体会。

2)装饰模式也有缺点:会增加我们所要维护类的数量。此模式一般与“工厂模式”一起使用,可克服此问题。

3)装饰模式可以达到运行时动态扩展系统功能的目的,通过不断扩展新的具体装饰类可以提高应对未来变化的能力。

 4)java IO类就是依照这个模式设计的

 

posted @ 2013-04-29 11:06  g.hui  阅读(248)  评论(0编辑  收藏  举报