table of Contents
Decorator pattern decorator pattern i.e., the class is decorated by the code described below.
Code demonstrates
Code shows the contents of
There is a class StringDisplay: express a word like hello world.
We decorated by decorator of this sentence - add a border around form | hello world |, plus the top and bottom border lines surrounded by these words, form
+-------------+
|Hello, world.|
+-------------+
UML diagrams
Interpretation of each class
Display categories: representatives 一段话的显示
. Using the 模版方法
model, defines an abstract method to obtain the number of columns, lines, etc., it specifies the use of these methods.
StringDisplay categories: representatives 一句话的显示
. Inheritance and implements all the abstract methods in the Display.
Border class: the representative 对一句话的装饰
. Inherited and commissioned Display category. It is an abstract decoration.
SideBorder categories: representatives 对某一行的左右添加装饰字符
. Border inherited class (meaning also inherited Display category), achieved their abstract methods.
FullBorder categories: representatives 对某一行的上下左右添加装饰字符
. And SideBorder class, and implements the abstract methods inherited Border class and the Display category.
Code
public abstract class Display {
public abstract int getColumns(); // 获取横向字符数
public abstract int getRows(); // 获取纵向行数
public abstract String getRowText(int row); // 获取第row行的字符串
public void show() { // 全部显示
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}
}
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;
}
}
}
public abstract class Border extends Display {
protected Display display; // 表示被装饰物
protected Border(Display display) { // 在生成实例时通过参数指定被装饰物
this.display = display;
}
}
public class SideBorder extends Border {
private char borderChar; // 表示装饰边框的字符
public SideBorder(Display display, char ch) { // 通过构造函数指定Display和装饰边框字符
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;
}
}
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) + "|";
}
}
private String makeLine(char ch, int count) { // 生成一个重复count次字符ch的字符串
StringBuffer buf = new StringBuffer();
for (int i = 0; i < count; i++) {
buf.append(ch);
}
return buf.toString();
}
}
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();
System.out.println();
b2.show();
System.out.println();
b3.show();
System.out.println();
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!!!|*||~
~||*+--------------+*||~
~|+------------------+|~
~+--------------------+~
*/
Role model and class diagram
Character
- Component: the decorative role, only defines API. In the present embodiment, a Display class play this role.
- ConcreteComponent: implements the Component API, is a specific role to be decorated. In the present embodiment, the class StringDisplay play this role.
- Decorator: decorator having the same Component API, decorative objects are retained inside Component. In the present embodiment, the Border class play this role.
- ConcreteDecorator: specific decorators. In this case by the SideBorder and FullBorder play this role.
Class Diagram
Expand ideas
Interface (API) transparency
Decorator继承了Component,装饰物和被装饰物具有一致性
——他们有着相同的API接口。即便API接口被装饰了一遍,也不会被隐藏起来,其他类依然可以调用被装饰后的API接口。可以用这个特性实现递归装饰。
为什么使用继承和委托
使用继承,是为了获得一致性,如上所说。
为什么使用委托呢?
如果只有继承,由于Decorator继承的是抽象的被装饰类,意味着我们要再实现一遍被装饰者的API,一旦被装饰类的API的逻辑发生改变,被装饰者也要改一次。
那如果Decorator继承的是具体的被装饰类呢?这样可以实现类似于委托那样的实现,直接调用父类的方法就可以了。
这样做的坏处至少有一个:有多少个具体的被装饰类,就要写多少遍装饰类。麻烦死了,而且重复代码一堆,绝不是什么好事情。
所以还是用委托吧。
java.io包和装饰者模式
//读取文件
Reader reader = new FileReader("xxxx.txt");
//读取时将文件放入缓冲区
Reader reader = new BufferReader(
new FileReader("xxxx.txt")
);
//还要管理行号
Reader reader = new LineNumberReader(
new BufferReader(
new FileReader("xxxx.txt")
)
);
...
缺点
增加很多功能类似的很小的修饰类