• 初识设计模式(装饰者模式)


     前言:总结这两天学到的装饰者模式,并用java小小的实现一下。书中有写到:给爱用继承的人一个全新的设计眼界。(ps,本文最后有个小问题待解决)

    什么是装饰者模式(Decorator Pattern)?

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

    使用的设计原则:开放-关闭原则,类应该对扩展开放,对修改关闭。

    代表:Java IO 流

    类图:

    装饰者模式的优缺点?

      优点

    1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
    2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

      缺点

    1. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂,也会给使用此API的程序员造成困扰。
    2. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
    3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
    4. 采用装饰者在实例化组件时,将增加代码的难度。一旦使用装饰者模式,不只需要实例化组件,还要把此组件包装进装饰者中。(这个问题可以使用工厂(Factory)模式和生成器(Builder)模式来解决)

    什么情况下使用装饰者模式?

    1. 需要扩展一个类的功能,或给一个类添加附加职责。
    2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
    3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
    4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

    举例说明

      场景:现在需要炒一盘菜,可以给这盘菜加多种作料,这盘菜也可以有不同作料的组合。

      实现:(如果只有一盘菜存在,不会再有第二盘菜的情况,也可以不创建抽象组件,直接创建具体组件,抽象的装饰类继承具体组件。)

    • 先创建抽象组件
       1 /**
       2  * 抽象组件 一盘菜
       3  */
       4 abstract class Dish{
       5     protected String name;
       6 
       7     public String getName(){
       8         return this.name;
       9     }
      10 }
    • 然后创建具体组件
      1 /**
      2  * 具体组件
      3  */
      4 class MeatDish extends Dish{
      5     public MeatDish(){
      6         super.name = "meatDish";
      7     }
      8 }
    • 然后创建抽象装饰类
      1 /**
      2  * 抽象装饰类 作料
      3  */
      4 abstract class Seasoning extends Dish{
      5     @Override
      6     public abstract String getName();
      7 }
    • 然后创建抽象具体装饰类
       1 /**
       2  * 具体装饰类 盐
       3  */
       4 class Salt extends Seasoning{
       5     private Dish dish;
       6     public Salt(Dish dish){
       7         this.dish = dish;
       8     }
       9     @Override
      10     public String getName() {
      11         return this.dish.getName() + " ,Salt";
      12     }
      13 }
      14 
      15 /**
      16  * 具体装饰类,鸡精
      17  */
      18 class ChickenPowder extends Seasoning{
      19     private Dish dish;
      20     public ChickenPowder(Dish dish){
      21         this.dish = dish;
      22     }
      23     @Override
      24     public String getName() {
      25         return this.dish.getName() + " ,ChickenPowder";
      26     }
      27 }
    • 然后测试
      1 public static void main(String[] args){
      2         Dish dish = new MeatDish();
      3         System.out.println(dish.getName());
      4 
      5         dish = new Salt(dish);
      6         dish = new ChickenPowder(dish);
      7         System.out.println(dish.getName());
      8     }
    • 最后输出结果

    总结:

    1. 装饰者和被装饰者拥有相同的超类
    2. 你可以用一个或多个装饰者包装一个对象
    3. 既然装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它
    4. 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的
    5. 对象可以在任何时候被装饰,所有可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象

    提问:

      问:不是说这个让爱用继承的人改变眼界吗?怎么类图还是使用的继承?

      答: 这么做的重点在于,装饰者和被装饰者必须是一样的类型,也就是有共同的超类,这是相当关键的地方。在这里,是利用继承达到“类型匹配”,而不是利用继承获得“行为”。

      问:那么行为从哪里来?

      答:当我们将装饰者与组件组合时,就是在加入新的行为。所得到的新行为,并不是继承自超类,而是由组合对象得来的。如果这里依赖继承,那么类的行为只能只能在编译时静态决定。换句话说,行为如果不是来自超类,就是子类覆盖后的版本。反之,利用组合,可以把装饰者混合着用,而且是在运行时。这样,就可以在任何时候实现新的装饰者增加新的行为,如果依赖继承,每当需要新行为时,还得修改现有的代码。

      问:为什么Componet要设计为一个抽象类,而不是一个接口?

      答:通常装饰者模式是采用抽象类。(有大神知道的吗?还请不吝赐教)

    参考书籍:《Head First 设计模式》《大话设计模式》

  • 相关阅读:
    浅析影响一个网站的因素
    23种常用设计模式的UML类图
    各版本IE兼容问题,IE6,IE7,IE8,IE9,IE10,IE11
    47种常见的浏览器兼容性问题大汇总
    5个Sublime Text 的插件推荐
    网页设计师神器,快速生成网站配色、字型等风格的工具——Stylify Me
    免费的高分辨率图库——re:splashed 可用做网页背景、设计或桌面壁纸
    MySQL(18):Select- subquery子查询
    MySQL(17):Select-union(联合查询)使用注意事项
    MySQL(16):Select-union(联合查询)
  • 原文地址:https://www.cnblogs.com/yuxiaole/p/9220672.html
Copyright © 2020-2023  润新知