• 【设计模式】装饰者模式


    一、简介

      当我们需要对一个类(A)的功能进行扩展的时候,可以选择使用继承通过子类(B)来实现,但是如果后来又要对A类增加一些功能且其中一些功能原来我们在子类B中已经实现过了,这时候怎么办呢?
      这时候只能再用子类C继承A类并把代码重写一遍,然后子类就越来越多难以维护且有很多重复代码不够灵活
      我们知道通过利用组合委托可以在运行时具有继承行为的功能,且更加灵活,通过装饰者模式就可以写新代码添加新的功能而且无需修改现有的代码(A类中的代码)。

      再用一个例子解释装饰者模式,假如现在有一块蛋糕,如果只涂上奶油就是奶油蛋糕,如果再加上草莓就是草莓奶油蛋糕,无论是蛋糕、奶油蛋糕还是草莓蛋糕,它们的核心都是蛋糕。
      程序中的对象与蛋糕十分相似,首先有一个相当于蛋糕的对象,然后不断地装饰蛋糕一样地不断地对其增加功能,它就变成了使用目的更加明确的对象,这样不断地为对象添加装饰的设计模式就是装饰者模式

    很多项目都用到了装饰者模式,比如Java的I/O流FileInputStream,BufferedInputStream等、Mybatis框架中的CachingExecutor(有兴趣的可以去看看其源码)。

    二、示例

    示例程序是写一个给文字添加装饰边框的功能,比如下面这样。

    +--------------+
    |#Hello World.#|
    +--------------+
    

    类的功能表格

    名字 说明
    Display 用于显示字符串的抽象类
    StringDisplay 显示单行字符串的类
    Border 显示边框的抽象类
    SideBorder 用于显示左右边框的类
    FullBorder 用于显示左右边框的类
    Main 测试程序的类

    类图如下:

    类图
    类图

    Display

    1. /** 
    2. * @author 2YSP 
    3. * @date 2020/6/16 21:07 
    4. */ 
    5. public abstract class Display
    6.  
    7. /** 
    8. * 获取横向字符数 
    9. * 
    10. * @return 
    11. */ 
    12. protected abstract int getColumns()
    13.  
    14. /** 
    15. * 获取纵向行数 
    16. * 
    17. * @return 
    18. */ 
    19. protected abstract int getRows()
    20.  
    21. /** 
    22. * 获取第row行的字符串 
    23. * 
    24. * @param row 
    25. * @return 
    26. */ 
    27. protected abstract String getRowText(int row)
    28.  
    29. /** 
    30. * 显示全部(用到了模板方法) 
    31. */ 
    32. public final void show()
    33. for (int i = 0; i < getRows(); i++) { 
    34. System.out.println(getRowText(i)); 

    StringDisplay

    1. /** 
    2. * @author 2YSP 
    3. * @date 2020/6/16 21:16 
    4. */ 
    5. public class StringDisplay extends Display
    6.  
    7. private String string; 
    8.  
    9. public StringDisplay(String string)
    10. this.string = string; 
    11.  
    12.  
    13. @Override 
    14. protected int getColumns()
    15. return string.getBytes().length; 
    16.  
    17. @Override 
    18. protected int getRows()
    19. return 1
    20.  
    21. @Override 
    22. protected String getRowText(int row)
    23. if (row == 0){ 
    24. return string; 
    25. }else
    26. return null

    Border

    1. public abstract class Border extends Display
    2. /** 
    3. * 被装饰物 
    4. */ 
    5. protected Display display; 
    6. // 在生成实例时通过参数指定被装饰物 
    7. protected Border(Display display)
    8. this.display = display; 

    SideBorder

    1. /** 
    2. * 装饰字符串的两侧,如|被装饰物| 
    3. * @author 2YSP 
    4. * @date 2020/6/16 21:21 
    5. */ 
    6. public class SideBorder extends Border
    7. private char borderChar;// 修饰边框的字符 
    8.  
    9. protected SideBorder(Display display, char ch)
    10. super(display); 
    11. this.borderChar = ch; 
    12.  
    13.  
    14. @Override 
    15. protected int getColumns()
    16. // 字符数=字符串字符数加两侧边框字符数 
    17. return 1 + display.getColumns() + 1
    18.  
    19. @Override 
    20. protected int getRows()
    21. return display.getRows(); 
    22.  
    23. @Override 
    24. protected String getRowText(int row)
    25. return borderChar + display.getRowText(row) + borderChar; 
    26.  

    FullBorder

    1. /** 
    2. * @author 2YSP 
    3. * @date 2020/6/16 21:26 
    4. */ 
    5. public class FullBorder extends Border
    6.  
    7.  
    8. protected FullBorder(Display display)
    9. super(display); 
    10.  
    11. @Override 
    12. protected int getColumns()
    13. return 1 + display.getColumns() + 1
    14.  
    15. @Override 
    16. protected int getRows()
    17. // 行数为被装饰物的行数加上上下边框的行数 
    18. return 1 + display.getRows() + 1
    19.  
    20. @Override 
    21. protected String getRowText(int row)
    22. if (row == 0) { 
    23. // 下边框 
    24. return "+" + makeLine('-', display.getColumns()) + "+"
    25. } else if (row == display.getRows() + 1) { 
    26. // 上边框 
    27. return "+" + makeLine('-', display.getColumns()) + "+"
    28. } else
    29. // 其他边框 
    30. return "|" + display.getRowText(row - 1) + "|"
    31.  
    32. private String makeLine(char ch, int count)
    33. StringBuilder builder = new StringBuilder(); 
    34. for (int i = 0; i < count; i++) { 
    35. builder.append(ch); 
    36. return builder.toString(); 
    37.  

    Main类

    1. public class Main
    2.  
    3. public static void main(String[] args)
    4. Display b1 = new StringDisplay("Hello World."); 
    5. Display b2 = new SideBorder(b1, '#'); 
    6. Display b3 = new FullBorder(b2); 
    7. b1.show(); 
    8. b2.show(); 
    9. b3.show(); 
    10.  
    11. Display b4 = new SideBorder(new FullBorder(new FullBorder( 
    12. new SideBorder(new FullBorder(new StringDisplay(" 你好,世界。")), '*'
    13. )), '/'); 
    14. b4.show(); 

    运行main方法控制打印结果如下:

    Hello World.
    #Hello World.#
    +--------------+
    |#Hello World.#|
    +--------------+
    /+-------------------------+/
    /|+-----------------------+|/
    /||*+-------------------+*||/
    /||*| 你好,世界。|*||/
    /||*+-------------------+*||/
    /|+-----------------------+|/
    /+-------------------------+/
    

    Decorator模式中的角色:

    • Component

    增加功能时的核心角色,装饰前的蛋糕就是Component角色,示例中的Display也是。

    • ConcreteComponent

    该角色是实现了Component角色所定义接口的具体蛋糕,示例中就是StringDisplay。

    • Decorator(装饰物)

    该角色与Component角色具有相同的API,在它内部保存了被装饰对象(Component角色),示例中就是Border。

    • ConcreteDecorator

    具体装饰物,示例中的FullBorder和SideBorder

    Decorator模式类图
    Decorator模式类图

    三、总结

    目前还没在实际项目中用到过这种设计模式,但是存在即合理肯定是有用的,代码已上传Github点击这里查看。

  • 相关阅读:
    第二次结对编程作业
    团队项目-选题报告
    第一次结对编程作业
    第一次个人编程作业
    软件工程实践作业(1)
    C博客作业01--分支丶顺序结构
    C博客作业00--我的第一篇博客
    C博客作业
    C博客作业01--分支、顺序结构
    一条简单select经过了多少路程
  • 原文地址:https://www.cnblogs.com/2YSP/p/13154957.html
Copyright © 2020-2023  润新知