背景
不断为对象添加装饰的设计模式被称为 装饰器模式 。首先有一个相当于蛋糕的对象,然后不断地装饰蛋糕一样地不断对其增加功能,它就变成了使用目的更加明确的对象
登场角色
Compoent 被装饰的对象
增加功能的核心角色,例如示例程序中的 Display 类
ConcreteCompoent 具体被装饰的对象
该角色是实现了 Component 角色所定义的接口的具体角色,例如示例程序的 StringDisplay 类
Decorator 装饰物
该角色具有和Compoent 角色相同的接口,在它内部保存了被装饰的对象-Component角色。Decorator角色知道自己要装饰的对象,例如示例程序中的 border 类
ConcreteDecorator 具体的装饰物
该角色是具体的Decorator角色,在示例程序中,由 SiderBorder 和 FullBorder 角色扮演
类图
示例代码
该示例程序的功能是显示一个字符串,并在它周围装饰不同的边框
Display 类
public abstract class Display {
public abstract int getRows();
public abstract int getColumns();
public abstract String getRowText(int row);
public void show() {
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}
}
StringDisplay类
public class StringDisplay extends Display{
private String text;
public StringDisplay(String text) {
this.text = text;
}
@Override
public int getRows() {
return 1;
}
@Override
public int getColumns() {
return text.getBytes().length;
}
@Override
public String getRowText(int row) {
if (row == 0) {
return text;
}
return null;
}
}
Border类
public abstract class Border extends Display {
// 被装饰物
public Display display;
public Border(Display display) {
this.display = display;
}
}
SideBorder类:在字符串左右添加边框
public class SideBorder extends Border{
private char aChar;
public SideBorder(Display display, char aChar) {
super(display);
this.aChar = aChar;
}
@Override
public int getRows() {
return display.getRows();
}
@Override
public int getColumns() {
return 1 + display.getColumns() + 1;
}
@Override
public String getRowText(int row) {
return aChar + display.getRowText(row) + aChar;
}
}
FullBorder类:在字符串上下左右添加边框
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
@Override
public int getRows() {
return 1 + display.getRows() + 1;
}
@Override
public int getColumns() {
return 1 + display.getColumns() + 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 c, int count) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < count; i++) {
stringBuffer.append(c);
}
return stringBuffer.toString();
}
}
功能分析
- 关键的一步就是把被装饰的对象聚合到装饰器中;
- 在Decorator模式中,被装饰物和装饰物具有一致性。因为他们都继承了Component抽象类,这样即使装饰物被被装饰物装饰起来,接口(API)也不会被隐藏起来,其他类依然可以调用抽象类中的方法,这就是接口API的透明性;
- 在不改变被装饰物的前提下增加功能,因为被装饰物和装饰物都实现了相同的接口,使用不同的装饰物就可以装饰不同的物品,实现不同的功能,实现不改变了装饰物的代码;
- Decorator 模式的缺点就是会导致一些功能类似的很小的类;
使用实例
在 java.io 包下的FileInputstream 和 InputStream 中使用了 Decorator 模式
延伸扩展:继承和委托中的一致性
一致性:可以将不同的东西当作同一种东西看待
继承 —— 父类和子类的一致性
child 实例 可以被保存在父类的类型变量中,调用父类的方法
public class Test {
public static void main(String[] args) {
Parent obj = new Child();
obj.parentMethod();
}
}
通过类型转换,可以调用子类的方法
public class Test {
public static void main(String[] args) {
Parent obj = new Child();
((Child)obj).childMethod();
}
}
委托 —— 自己和被委托对象的一致性
定义一个公共接口
public interface Flower {
void method();
}
定义两个类 Rose Violet,Rose把method方法委托给Violet
public class Violet implements Flower{
@Override
public void method() {
}
}
public class Rose implements Flower {
Violet violet;
@Override
public void method() {
violet.method();
}
}