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


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

    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、虽然比继承具有很高的灵活性,但是也意味着出错的可能性越高,排错比较麻烦。

  • 相关阅读:
    Storm
    Linux 网络配置
    .NET Remoting
    jwt
    new操作符
    元数据
    C# lock
    三十而立
    面试
    灯火
  • 原文地址:https://www.cnblogs.com/miaowu1314/p/5482527.html
Copyright © 2020-2023  润新知