Author:Gorit
Date:2021年10月17日
Refer:《图解设计模式》

12.1 Decorator

相片 与 相册(装饰边框 与 被装饰物 的一致性)

在产品中,装饰即不断的在产品中增加新功能。

不断为对象添加装饰的设计模式为 Decorator

12.2 示例程序

本章我们做一个给字符串添加“边框”的功能

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

一、Display 类

  1. package Decorator;
  2. /**
  3. * @Author Gorit
  4. * @Date 2021/10/17
  5. * @desc 用于显示多行字符串的抽象类
  6. *
  7. **/
  8. public abstract class Display {
  9. public abstract int getColumns(); // 获取横向字符数
  10. public abstract int getRows(); // 获取纵向行数 Template Method
  11. public abstract String getRowText(int row); // 获取第 row 行字符串 Template Method
  12. public final void show () { // 全部显示
  13. for (int i = 0; i < getRows(); i++) {
  14. System.out.println(getRowText(i));
  15. }
  16. }
  17. }

二、 StringDisplay

  1. package Decorator;
  2. /**
  3. * @Author Gorit
  4. * @Date 2021/10/17
  5. * 代表核心的被装饰物
  6. **/
  7. public class StringDisplay extends Display {
  8. private String string; // 要显示的字符串
  9. public StringDisplay(String string) { // 通过参数传入要显示的字符串
  10. this.string = string;
  11. }
  12. public int getColumns() { // 获取字符数
  13. return string.getBytes().length;
  14. }
  15. public int getRows() { // 默认只显示 1 行
  16. return 1;
  17. }
  18. public String getRowText(int row) { // 仅当 row 为 0时,返回
  19. if (row == 0) {
  20. return string;
  21. } else {
  22. return null;
  23. }
  24. }
  25. }

三、Border

  1. package Decorator;
  2. /**
  3. * @Author Gorit
  4. * @Date 2021/10/17
  5. * Border 和 Display 具有相同的属性和方法
  6. * display 代表被装饰物,也有可能是其他装饰物的边框
  7. **/
  8. public abstract class Border extends Display {
  9. protected Display display; // 表示被装饰物
  10. protected Border(Display display) { // 在生成实例时通过参数指定被装饰物
  11. this.display = display;
  12. }
  13. }

四、SideBorder

  1. package Decorator;
  2. import javafx.geometry.Side;
  3. /**
  4. * @Author Gorit
  5. * @Date 2021/10/17
  6. * 具体的装饰边框
  7. **/
  8. public class SideBorder extends Border {
  9. private char borderChar; // 表示装饰边框的字符
  10. public SideBorder(Display display, char ch) {
  11. super(display);
  12. this.borderChar = ch; // 具体的边框
  13. }
  14. public int getColumns() { // 字符数为字符串字符数列加上两侧边框字符数
  15. return 1 + display.getColumns() + 1;
  16. }
  17. public int getRows() {
  18. return display.getRows(); // 行数,即被修饰的行数
  19. }
  20. public String getRowText(int row) { // 指定的那一行的字符串为被装饰物字符串, 加上两侧的边框字符
  21. return borderChar + display.getRowText(row) + borderChar;
  22. }
  23. }

五、FullBorder

  1. package Decorator;
  2. /**
  3. * @Author Gorit
  4. * @Date 2021/10/17
  5. * 字符串左右加修饰,SliderBorder 类中可以指定边框字符,FullSlider 类中边框字符是固定的
  6. **/
  7. public class FullBorder extends Border {
  8. public FullBorder(Display display) {
  9. super(display);
  10. }
  11. public int getColumns() { // 字符数为被装饰的字符数加上两侧边框的字符数
  12. return 1 + display.getColumns() + 1;
  13. }
  14. public int getRows() { // 行为被装饰物的行数加上上下边框的行数
  15. return 1 + display.getRows() +1;
  16. }
  17. public String getRowText(int row) { // 指定的那一行字符串
  18. if (row == 0) { // 下边框
  19. return "+" + makeLine('-', display.getColumns()) + "+";
  20. } else if (row == display.getRows() + 1) { // 上边框
  21. return "+" + makeLine('-', display.getColumns()) + "+";
  22. } else { // 其他边框
  23. return "|" + display.getRowText(row - 1) + "|";
  24. }
  25. }
  26. // 生成一个重复 count 次字符 ch 的字符串
  27. private String makeLine(char ch, int count) {
  28. StringBuffer bf = new StringBuffer();
  29. for (int i = 0; i < count; i++) {
  30. bf.append(ch);
  31. }
  32. return bf.toString();
  33. }
  34. }

六、Main

  1. package Decorator;
  2. /**
  3. * @Author Gorit
  4. * @Date 2021/10/17
  5. **/
  6. public class Main {
  7. public static void main(String[] args) {
  8. Display b1 = new StringDisplay("Hello , World"); // 被修饰物
  9. Display b2 = new SideBorder(b1, '#');
  10. Display b3 = new FullBorder(b2);
  11. b1.show();
  12. b2.show();
  13. b3.show();
  14. Display d4 =
  15. new SideBorder(
  16. new FullBorder(
  17. new FullBorder(
  18. new SideBorder(
  19. new FullBorder(
  20. new StringDisplay("你好,世界")
  21. ),
  22. '*'
  23. )
  24. )
  25. ),
  26. '/'
  27. );
  28. d4.show();
  29. }
  30. }

七、效果

Hello , World b1.show() 显示结果
#Hello , World# b2.show() 显示结果
+———————-+ b3.show() 显示结果
|#Hello , World#|
+———————-+
/+——————————-+/ b4.show() 显示结果
/|+—————————-+|/
/||+———————-+||/
/|||你好,世界|||/
/||+———————-+||/
/|+—————————-+|/
/+——————————-+/

12.3 Decorator 模式中登场的角色

一、Component

增加功能时的核心角色,在本次案例中,装饰前的的蛋糕就是 Component 角色。该角色只是定义了蛋糕的接口(API)。Display 担任此角色

二、 ConcreateComponent

实现 Component 所定义的接口(API),示例程序中由 StringDisplay 担任此角色

三、Decorator(装饰物)

该角色与 Component 具有相同的接口(API),它内部保存了被装饰的对象 —— Component 角色。 Decorator 角色知道自己要装饰的对象。Border 扮演此角色

四、ConcreateDecorator(具体的装饰物)

该角色是具体的 Decorator 角色。由 SideBorder 和 FullBorder 担任此角色

12.4 继承和委托的一致性

  1. 继承 —— 父类与子类一致性