Author:Gorit
Date:2021年10月17日
Refer:《图解设计模式》
12.1 Decorator
相片 与 相册(装饰边框 与 被装饰物 的一致性)
在产品中,装饰即不断的在产品中增加新功能。
不断为对象添加装饰的设计模式为 Decorator
12.2 示例程序
本章我们做一个给字符串添加“边框”的功能
名字 | 说明 |
---|---|
Display | 用于显示字符串的抽象类 |
StringDisplay | 用于显示单行字符串的类 |
Border | 用于显示边框的抽象类 |
FullBorder | 用于显示上下左右边框的类 |
Main | 测试程序行为的类 |
一、Display 类
package Decorator;
/**
* @Author Gorit
* @Date 2021/10/17
* @desc 用于显示多行字符串的抽象类
*
**/
public abstract class Display {
public abstract int getColumns(); // 获取横向字符数
public abstract int getRows(); // 获取纵向行数 Template Method
public abstract String getRowText(int row); // 获取第 row 行字符串 Template Method
public final void show () { // 全部显示
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}
}
二、 StringDisplay
package Decorator;
/**
* @Author Gorit
* @Date 2021/10/17
* 代表核心的被装饰物
**/
public class StringDisplay extends Display {
private String string; // 要显示的字符串
public StringDisplay(String string) { // 通过参数传入要显示的字符串
this.string = string;
}
public int getColumns() { // 获取字符数
return string.getBytes().length;
}
public int getRows() { // 默认只显示 1 行
return 1;
}
public String getRowText(int row) { // 仅当 row 为 0时,返回
if (row == 0) {
return string;
} else {
return null;
}
}
}
三、Border
package Decorator;
/**
* @Author Gorit
* @Date 2021/10/17
* Border 和 Display 具有相同的属性和方法
* display 代表被装饰物,也有可能是其他装饰物的边框
**/
public abstract class Border extends Display {
protected Display display; // 表示被装饰物
protected Border(Display display) { // 在生成实例时通过参数指定被装饰物
this.display = display;
}
}
四、SideBorder
package Decorator;
import javafx.geometry.Side;
/**
* @Author Gorit
* @Date 2021/10/17
* 具体的装饰边框
**/
public class SideBorder extends Border {
private char borderChar; // 表示装饰边框的字符
public SideBorder(Display display, char ch) {
super(display);
this.borderChar = ch; // 具体的边框
}
public int getColumns() { // 字符数为字符串字符数列加上两侧边框字符数
return 1 + display.getColumns() + 1;
}
public int getRows() {
return display.getRows(); // 行数,即被修饰的行数
}
public String getRowText(int row) { // 指定的那一行的字符串为被装饰物字符串, 加上两侧的边框字符
return borderChar + display.getRowText(row) + borderChar;
}
}
五、FullBorder
package Decorator;
/**
* @Author Gorit
* @Date 2021/10/17
* 字符串左右加修饰,SliderBorder 类中可以指定边框字符,FullSlider 类中边框字符是固定的
**/
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
public int getColumns() { // 字符数为被装饰的字符数加上两侧边框的字符数
return 1 + display.getColumns() + 1;
}
public int getRows() { // 行为被装饰物的行数加上上下边框的行数
return 1 + display.getRows() +1;
}
public String getRowText(int row) { // 指定的那一行字符串
if (row == 0) { // 下边框
return "+" + makeLine('-', display.getColumns()) + "+";
} else if (row == display.getRows() + 1) { // 上边框
return "+" + makeLine('-', display.getColumns()) + "+";
} else { // 其他边框
return "|" + display.getRowText(row - 1) + "|";
}
}
// 生成一个重复 count 次字符 ch 的字符串
private String makeLine(char ch, int count) {
StringBuffer bf = new StringBuffer();
for (int i = 0; i < count; i++) {
bf.append(ch);
}
return bf.toString();
}
}
六、Main
package Decorator;
/**
* @Author Gorit
* @Date 2021/10/17
**/
public class Main {
public static void main(String[] args) {
Display b1 = new StringDisplay("Hello , World"); // 被修饰物
Display b2 = new SideBorder(b1, '#');
Display b3 = new FullBorder(b2);
b1.show();
b2.show();
b3.show();
Display d4 =
new SideBorder(
new FullBorder(
new FullBorder(
new SideBorder(
new FullBorder(
new StringDisplay("你好,世界")
),
'*'
)
)
),
'/'
);
d4.show();
}
}
七、效果
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 担任此角色