• 装饰者模式


          装饰者模式能够动态地将责任附加到对象上,在扩展对象功能方面比继承更加灵活,具体来说,装饰者模式将行为委托给相应的包装对象,并添加上自己的对应逻辑来实现特定的功能。装饰者模式的UML图如下:

          首先需要有被装饰的组件接口和具体组件,然后有装饰者对象,由于装饰者对象需要能够代替组件,所以要继承组件接口,并组合组件对象来完成委托任务。

          下面以一个简单的快餐店为例子来介绍装饰者模式的用法。快餐店会有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。举个极端的例子,某个顾客一下子加了20中配菜,要计算总共需要多少钱估计还得拿个计算器出来算一会儿,这种情况下装饰者模式就有了用武之地。

    按照上面的UML类图,先定义组件接口(快餐)和具体组件(炒饭、炒面)。

     1 //快餐接口
     2 public interface FastFood {
     3   float getCost();  //获取价格
     4   String getDescription();  //获取描述
     5 }
     6 
     7 //炒饭
     8 public class FriedRice implements FastFood{
     9   private float price = 5;
    10   String description = "炒饭";
    11   @Override
    12   public float getCost() {
    13     return this.price;
    14   }
    15 
    16   @Override
    17   public String getDescription() {
    18     return this.description;
    19   }
    20 }
    21 
    22 //炒面
    23 public class FriedNoodles implements FastFood{
    24   private float price = 5;
    25   String description = "炒面";
    26   @Override
    27   public float getCost() {
    28     return this.price;
    29   }
    30 
    31   @Override
    32   public String getDescription() {
    33     return this.description;
    34   }
    35 }

          接下来是配菜相关接口和类:

     1 //配菜
     2 public interface Garnish extends FastFood{
     3    String getDescription();
     4 }
     5 
     6 //鸡蛋
     7 public class Egg implements Garnish{
     8   float price = 1.5f;
     9   String description = "鸡蛋";
    10   private FastFood fastFood;
    11 
    12   public Egg(FastFood fastFood){
    13     this.fastFood = fastFood;
    14   }
    15 
    16   @Override
    17   public float getCost() {
    18     return this.price + fastFood.getCost();
    19   }
    20 
    21   @Override
    22   public String getDescription() {
    23     return this.description + fastFood.getDescription();
    24   }
    25 }
    26 
    27 //培根
    28 public class Bacon implements Garnish{
    29   private float price = 2f;
    30   private String description = "培根";
    31   private FastFood fastFood;
    32 
    33   public Bacon(FastFood fastFood){
    34     this.fastFood = fastFood;
    35   }
    36 
    37   @Override
    38   public float getCost() {
    39     return this.price + fastFood.getCost();
    40   }
    41 
    42   @Override
    43   public String getDescription() {
    44     return this.description + fastFood.getDescription();
    45   }
    46 }    
    47 
    48 //火腿
    49 public class Ham implements Garnish{
    50   float price = 1f;
    51   String description = "火腿";
    52   private FastFood fastFood;
    53 
    54   public Ham(FastFood fastFood){
    55     this.fastFood = fastFood;
    56   }
    57 
    58   @Override
    59   public float getCost() {
    60     return this.price + fastFood.getCost();
    61   }
    62 
    63   @Override
    64   public String getDescription() {
    65     return this.description + fastFood.getDescription();
    66   }
    67 }

          接下来就可以点几份快餐测试一下代码了:

     1 public class DecoratorPatternTest {
     2   public static void main(String[] args){
     3     FastFood friedRice = new FriedRice();
     4     System.out.println(friedRice.getDescription() + " " + friedRice.getCost() + "元");
     5     FastFood friedNoodles = new FriedNoodles();
     6     friedNoodles = new Egg(friedNoodles);
     7     friedNoodles = new Ham(friedNoodles);
     8     System.out.println(friedNoodles.getDescription() + " " + friedNoodles.getCost() + "元");
     9     friedRice = new Bacon(friedRice);
    10     System.out.println(friedRice.getDescription() + " " + friedRice.getCost() + "元");
    11   }
    12 }

          输出如下:

    炒饭 5.0元
    火腿鸡蛋炒面 7.5元
    培根炒饭 7.0元

          现在来回顾一下上面的类结构,我们在点培根炒饭时,先创建一个炒饭对象,然后用培根对象把炒饭包装了一下,当计算价格以及输出描述时,我们的做法是获取装饰者(培根)对象的价格和描述,同时需要获取组件(炒饭)的价格我描述,我们将这项任务委托给组件来完成,火腿鸡蛋炒面也是同样的道理。装饰者继承组件,使得装饰者可以包装任意的具体组件,同样也可以包装装饰者;同时,装饰者也可以加入自己的逻辑,给组件增添不一样的行为,例如这里在技术价格以及获取描述时,除了返回装饰者自己的属性,还增加了返回组件属性的逻辑。

          其实装饰者模式在Java API中使用的很多,举个很简单的例子,当我们需要读取文件时,很可能会写出下面的代码:

    1 BufferedReader bf = new BufferedReader(new FileReader(new File("./test.txt")));

          其中BufferedReader和FileReader就充当了装饰者的作用,而File则是被装饰的组件,还有一些其他的API也用到了装饰者模式,需要大家自己去摸索了。

          参考文献:

          <<Head First设计模式>>

  • 相关阅读:
    【bzoj4917】Hash Killer IV 乱搞
    【bzoj2770】YY的Treap 权值线段树
    【bzoj4898】[Apio2017]商旅 Floyd+分数规划+Spfa
    【GXZ的原创】平衡树性能测试
    【bzoj2969】矩形粉刷 期望
    【bzoj3325】[Scoi2013]密码 逆模拟Manacher
    【bzoj4974】字符串大师 逆模拟KMP
    【bzoj4987】Tree 树形背包dp
    【bzoj3672】[Noi2014]购票 斜率优化dp+CDQ分治+树的点分治
    【bzoj4325】NOIP2015 斗地主(&“加强”版) 搜索
  • 原文地址:https://www.cnblogs.com/NaLanZiYi-LinEr/p/11605616.html
Copyright © 2020-2023  润新知