【笔记整理】图解设计模式 | 第12章 Decorator模式(装饰边框与被装饰物的一致性)
作者:互联网
定义
- 不断地为对象添加装饰的设计模式被称为Decorator模式,其中Decorator指的是“装饰物”。
Decorator模式中的登场角色
- Component(API,抽象)
Component角色定义了公共的接口(API)。
- ConcreteComponent(被装饰物)
该角色是实现了Component角色所定义的接口(API)的具体类。
- Decorator(装饰物,抽象)
该角色具有与Component角色相同的接口(API)。在它内部保存了被装饰对象——Component角色。Decorator角色知道自己要装饰的对象。
- ConcreteDecorator(具体的装饰物)
该角色是具体的Decorator角色。
Decorator模式的类图
关键点:
- 装饰物是API公共接口的子类。
- 装饰物中有一个被装饰物的实例,并以构造函数形式进行初始化。
- 方法执行顺序:首先执行装饰物的方法,再执行被装饰物的方法,以达到装饰的目的。
拓展思路的要点
- 接口(API)的透明性
在Decorator模式中,装饰边框与被装饰物具有一致性。表示装饰边框的类是表示被装饰物的类的子类,体现了它们之间的一致性,它们具有相同的接口(API)。
这样,即使被装饰物被边框装饰起来了,接口(API)也不会被隐藏起来。其他类依然可以调用接口的方法。这就是接口(API)的透明性。
得益于接口(API)的透明性,Decorator模式中也形成了类似于Composite模式中的递归结构。也就是说,装饰边框里面的“被装饰物”实际上又是别的物体的“装饰边框”。就像是剥洋葱时以为洋葱心要出来了,结果却发现还是皮。
不过,Decorator模式虽然与Composite模式一样,都具有递归结构,但是它们的使用目的不同。Decorator模式的主要目的是通过添加装饰物来增加对象的功能。Composite模式的目的拥有关系(Composite模式是树形结构,Decorator模式俄罗斯套娃。)。
- 在不改变被装饰物的前提下增加功能
在Decorator模式中,装饰边框与被装饰物具有相同的接口(API)。虽然接口(API)是相同的,但是越装饰,功能则越多。这样,我们就实现了不修改被装饰的类即可增加功能。
Decorator模式使用了委托。对“装饰边框”提出的要求(调用装饰边框的方法)会被转交(委托)给“被装饰物”去处理。
- 可以动态地增加功能
Decorator模式中用到了委托,它使类之间形成了弱关联关系。因此,不用改变框架代码,就可以生成一个与其他对象具有不同关系的新对象。
- 只需要一些装饰物即可添加许多功能
可以将一些功能非常简单的装饰边框,自由组合成为新的对象。
- Java.io包与Decorator模式
除了java.io包以外,javax.swing.border包中也使用了Decorator模式,提供了可以为界面中的控件比那家装饰边框的类。
相关的设计模式
Decorator模式可以在不改变被装饰物的接口(API)的前提下,为被装饰物添加边框(透明性)。
Adapter模式用于适配两个不同的接口(API)。
Decorator模式可以像改变被装饰物的边框或是被装饰物添加多重边框那样,来增加类的功能。
Stragety模式通过整体地替换算法来改变类的功能。
延伸阅读:继承和委托中的一致性
- 继承——父类和子类的一致性
- 委托——自己和被委托对象的一致性
代码
- Component(API)
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));
}
}
}
- ConcreteComponent(被装饰物)
/**
* 显示单行字符串
*/
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 (0 == row) {
return string;
}
return null;
}
}
- Decorator(装饰物)
public abstract class Border extends Display {
protected Display display;
public Border(Display display) {
this.display = display;
}
}
- ConcreteDecorator(具体的装饰物)
public class SideBorder extends Border {
private char borderChar;// 表示装饰边框的字符
public SideBorder(Display display, char ch) { // 通过构造函数执行Display和装饰边框字符
super(display);
borderChar = ch;
}
@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;
}
}
public class UpDownBorder extends Border {
private char borderChar;// 表示装饰边框的字符
public UpDownBorder(Display display, char ch) { // 通过构造函数执行Display和装饰边框字符
super(display);
borderChar = ch;
}
@Override
public int getColumns() {
return display.getColumns();
}
@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(int count) { // 生成一个重复count次字符ch的字符串
StringBuffer buf = new StringBuffer();
for (int i = 0; i < count; i++) {
buf.append(borderChar);
}
return buf.toString();
}
}
- Main
public class Main {
public static void main(String[] args) {
Display b1 = new StringDisplay("Hello, world.");
Display b2 = new UpDownBorder(b1, '-');
Display b3 = new SideBorder(b2, '*');
b1.show();
b2.show();
b3.show();
Display b4 = new FullBorder(new UpDownBorder(
new SideBorder(new UpDownBorder(new SideBorder(new StringDisplay("Hello, world."), '*'), '='), '|'),
'/'));
b4.show();
}
}
注:博客中的图片来自网上。
标签:12,display,边框,API,装饰物,设计模式,public,Decorator 来源: https://blog.csdn.net/jccg1000196340/article/details/88387023