开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情
装饰器模式
引入
假设有一块🎂,如果涂上奶油,就是奶油蛋糕。如果加上🍓,就是草莓奶油蛋糕。如果加上🍫,蜡烛,就变成了一块生日蛋糕。
不论时蛋糕,奶油蛋糕,草莓奶油蛋糕还是生日蛋糕,他们的核心都是蛋糕。
首先有一个相当于蛋糕的对象,然后不断的装饰当高一样的不断对其增加功能,他就变成了使用更加明确的对象。
像这样不断的为dui'xdui像添加功能的设计模式称为Decorator模式。 Decorator指的是“装饰物”。
事例程序
类的一览表
名字 | 说明 |
---|---|
Display | 用于显示字符串的抽象类 |
StringDisplay | 用于显示单行字符的类 |
Border | 用于显示装饰边框的抽象类 |
SideBorder | 用于只显示右边框的类 |
FullBorder | 用于显示上下左右边框的类 |
Main | 测试 |
类图
相关类
Display类
- 抽象类,显示多行字符串
- getColumns(),抽象方法,获取横向字符数
- getRows();抽象方法,获取纵向行数
- getRowText(int row);抽象方法,获取第row行的字符串
- show() 是显示所有行的字符串的方法,调用getRows()获取行数,调用getRowText(int row)获取需要显示的字符串。show方法使用了getRows和getRowText等抽象方法,这属于模板模式。
public abstract class Display {
public abstract int getColumns();//获取横向字符数
public abstract int getRows();//获取纵向行数
public abstract String getRowText(int row); //获取第row行的字符串
public final void show() {
//全部展示
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}
}
StringDisplay类
- 用于显示单行字符串的类。是Display类的子类,实现display的抽象方法。
- string保存要显示的字符串。StringDisplay只显示一行字符串,所以getColumns返回string.getBytes().length,getRows返回固定值1。
- 当获取第0行的时候getRowText才返回string字段。
- StringDisplay相当于生日蛋糕中的核心🎂。
public class StringDisplay extends Display {
private String string; //要显示的字符串
public StringDisplay(String string) { //传入要显示的字符串
this.string = string;
}
@Override
public int getColumns() { //字符数
return string.getBytes().length;
}
@Override
public int getRows() { //行数是1
return 1;
}
@Override
public String getRowText(int row) { //当row为0返回
if (row == 0) {
return string;
} else {
return null;
}
}
}
Border类
- 装饰边框的抽象类,Display的子类
- 通过继承,装饰边框与被装饰物有了相同的方法,具有了一致性
- Display类型的display字段,表示被装饰物。不过display并不仅限于StringDisplay的实例,因为Border也是Display的子类,display可能是其他装饰边框。
public abstract class Border extends Display {
protected Display display;//表示被装饰物
public Border(Display display) { //在生成实例时通过参数指定被装饰物
this.display = display;
}
}
SideBorder类
- 在字符串两侧装饰边框
效果:
|被装饰物|
public class SideBorder extends Border {
private char borderChar;
public SideBorder(Display display, char borderChar) {
super(display);
this.borderChar = borderChar;
}
@Override
public int getColumns() {
return 1 + display.getColumns() + 1;
}
@Override
public int getRows() {
return display.getRows();
}
@Override
public String getRowText(int row) {
return borderChar + display.getRowText(row) + borderChar;
}
}
FullBorder类
- 在字符串的上下左右都加上装饰边框
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
@Override
public int getColumns() {
return 1 + display.getColumns() + 1;
}
@Override
public int getRows() {
return 1 + display.getRows() + 1;
}
@Override
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) + "|";
}
}
private String makeLine(char ch, int count) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < count; i++) {
buf.append(ch);
}
return buf.toString();
}
}
Main
- b1,显示hello world
- b2,b1的两侧加上‘#’
- b3,b2上下左右加上装饰边框
- b4,给hello world加上多重边框
public class Main {
public static void main(String[] args) {
Display b1 = new StringDisplay("hello world");
b1.show();
Display b2 = new SideBorder(b1, '#');
b2.show();
Display b3 = new FullBorder(b1);
b3.show();
Display b4 = new SideBorder(
new FullBorder(
new FullBorder(
new SideBorder(
new FullBorder(
new StringDisplay("hello world")
),
'*'
)
)
)
, '/');
b4.show();
}
}
效果:
hello world
#hello world#
+-----------+
|hello world|
+-----------+
/+-----------------+/
/|+---------------+|/
/||*+-----------+*||/
/||*|hello world|*||/
/||*+-----------+*||/
/|+---------------+|/
/+-----------------+/
登场角色
-
Component
增加功能的核心角色,定义蛋糕的接口。——Display
-
ConcreteComponent
具体的蛋糕。——StringDisplay
-
Decorator
与Component具有相同的接口,在内部保存了被装饰的对象。——Border
-
ConcreteDecorator
具体的装饰角色。——SideBorder和FullBorder