深入浅出设计模式学习笔记三:装饰者模式

装饰者模式:动态的将责任附加到对象上

Java I/O API也是使用此模式的

装饰模式的类图如下:

 

抽象组件(Component):抽象类,也可以是接口,规范了动态接收责任的对象。

具体组件(Concrete Component):定义一个将要接收附加责任的类,该组件可以单独使用,或者是被装饰者包装起来使用。

抽象装饰组件(Decorator):抽象类,也可以是接口,拥有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

具体装饰组件(Concrete ComponentA,Concrete ComponentB):负责装饰具体的组件,给具体的组件添加责任,装饰者中有一个实例变量,可以记录被装饰者;装饰者可以扩展Component的状态,也可以加上新的方法,新行为是通过在旧行为前面或者后面做一些计算来添加的。

 

举例说明:小明想开一个饮品店,需要制定饮品的分类以及价格,饮品的主要原料是水,其他的原料我们称之为调料,包括牛奶,白砂糖,咖啡粉,红茶,水果等等,加的调料不一样,饮品的价格也不一样,各种材料的价格如下:

原料 价格(元)
1
牛奶 2
咖啡粉 2
红茶 1
绿茶 1
蔓越莓 2
柠檬 2
白砂糖 1

 

 

 

 

 

 

 

 

具体类图如下:

具体的代码如下所示:

  1 //抽象类,相当于类图中的抽象组件,所有的被装饰者都继承该类,并实现其中的抽象方法
  2 abstract class Drink{
  3     String description = "Unknow Drink";
  4     //定义获取描述的方法,返回变量description
  5     public String getDescription(){        
  6         return description;
  7         }
  8     //抽象方法,所有继承该抽象类的之类都需实现该方法
  9     public abstract int cost();
 10 }
 11 //具体实现类,相当于类图中的具体组件,一般认为是被装饰者
 12 class Water extends Drink{
 13    public Water(){
 14        description = "水";
 15    }   
 16     @Override
 17     public int cost() {
 18         return 1;
 19     }    
 20 }
 21 
 22 //抽象装饰组件
 23 abstract class Condiment extends Drink{
 24     Drink drink;
 25     public Condiment(Drink drink){
 26         this.drink = drink;
 27     } 
 28     public abstract String getDescription();
 29 }
 30 /**
 31  * 具体装饰组件:需要让装饰者能够持有被装饰者,做法如下:
 32  * 1:定义一个被装饰者的实例变量
 33  * 2:想办法让被装饰者记录在实例变量中,这里的做法是通过构造函数实现
 34  * @author SAMSUNG
 35  *
 36  */
 37 class RedTea extends Condiment{
 38     public RedTea(Drink tmpDrink){
 39         super(tmpDrink);
 40     }
 41     //获取完整的描述,除了自己的描述外,还通过委托,得到被装饰者的描述,两者结合得到完整的描述
 42     @Override
 43     public String getDescription() {        
 44         return drink.getDescription()+" 加红茶";
 45     }  
 46     @Override
 47     public int cost() {        
 48         return 2 + drink.cost();
 49     }
 50 }
 51 //具体装饰组件
 52 class Sugar extends Condiment{
 53     public Sugar(Drink tmpDrink){
 54         super(tmpDrink);
 55     }    
 56     @Override
 57     public String getDescription() {
 58         
 59         return drink.getDescription()+" 加白砂糖";
 60     }
 61     @Override
 62     public int cost() {
 63         return 1 + drink.cost() ;
 64     }
 65 }
 66 
 67 //具体装饰组件
 68 class Lemon extends Condiment{
 69   public Lemon(Drink tmpDrink){
 70       super(tmpDrink);
 71   }    
 72     @Override
 73     public String getDescription() {
 74         
 75         return drink.getDescription()+" 加柠檬";
 76     }
 77     @Override
 78     public int cost() {
 79         return 1 + drink.cost() ;
 80     }
 81 }
 82 
 83 
 84 //测试类
 85 public class DrinkTest {
 86     
 87     public static void main(String[] args) {
 88         
 89         Drink drink1 = new Water();
 90         //打印不添加任何装饰者的被装饰者的信息
 91         System.out.println(drink1.getDescription()+" 价钱:"+drink1.cost());
 92         //使用redtea装饰
 93         drink1 = new RedTea(drink1);
 94         System.out.println(drink1.getDescription()+" 价钱:"+drink1.cost());
 95         //使用sugar装饰
 96         drink1 = new Sugar(drink1);
 97         System.out.println(drink1.getDescription()+" 价钱:"+drink1.cost());
 98         //使用lemon装饰
 99         drink1 = new Lemon(drink1);
100         System.out.println(drink1.getDescription()+" 价钱:"+drink1.cost());
101     }
102 }

 

运行结果如下:

 

代码分析:

1、抽象类都可以换成接口,接口是一种极度抽象的类型,比抽象类更加抽象;

2、装饰抽象类(Condiment )持有一个被装饰者抽象类(Drink)的引用,通过构造函数,传入Drink的一个对象作为参数,并将该参数赋给引用,使得装饰者可以持有被装饰者;

3、所有具体的装饰者的构造函数都调用了super(tmpDrink)方法,具体实现依赖抽象,体现了依赖倒置原则,当有新的装饰者加入的时候,不需要修改代码,只需定义一个新的装类,并让该类继承装饰者抽象类,就可以成为一个新的具体装饰者,体现了对扩展开放,对修改关闭的设计原则;

4、由运行结果可知: 多个装饰者可以包装一个装饰者,叫做装饰者链;

5、装饰者(Condiment )与被装饰者(Water)具有相同的类型,这里虽然使用了继承,使用继承的的目的在于达到“类型匹配",而不是获得”行为“;如果依赖于继承,那么类的行为只能在编译时静态决定,反之,利用组合,可在运行时利用装饰者增加新的行为。

 

装饰者模式的优缺点:

优点:

1、可以给对象添加很多额外的行为,扩展对象的行为,比继承更具有灵活性;

2、通过不同的装饰类,或者多个装饰类的排列组合,在运行时改变对象的行为,使得对象具有更强大的功能;

缺点:

1、会产生很多的小对象,多度使用的话增加了系统的复杂性;

2、虽然比继承具有很高的灵活性,但是也意味着出错的可能性越高,排错比较麻烦。

 

posted @ 2016-06-03 16:59  喵呜1314  阅读(257)  评论(0编辑  收藏  举报