程序中的对象与蛋糕非常相似。首先有一个相当于蛋糕的对象,然后像不断地装饰蛋糕一样不断地对其增加功能,它就变成了使用目的更加明确的对象。<br />像这样不断地为对象添加装饰的设计模式被称为Decorator模式。
示例程序:
| 名字 | 说明 |
|---|---|
| Display | 用于显示字符串的抽象类 |
| StringDisplay | 用于显示单行字符串的类 |
| Border | 用于显示装饰边框的抽象类 |
| SideBorder | 用于只显示左右边框的类 |
| FullBorder | 用于显示上下左右边框的类 |
| Main | 测试程序行为的类 |
Display:
Display类是可以显示多行字符串的抽象类。
其中show是显示所有行的字符串的方法。在show方法内部,程序会调用getRows方法获取行数,调用get
RowText获取该行需要显示的字符串,然后通过for循环间所有的字符串显示出来。show方法使用了getRows和getRowText等抽象方法,属于Tempate Method模式。
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的变量存储该行字符串即可。
public class StringDisplay extends Display{private String string;public StringDisplay(String string) {this.string = string;}@Overridepublic int getColumns() {return string.getBytes().length;}@Overridepublic int getRows() {return 1;}@Overridepublic String getRowText(int row) {if (row ==0) {return string;}else {return null;}}}
Border类:
Border类是装饰边框的抽象类。虽然它所表示的是装饰边框,但它也是Display的子类。
原因是,通过继承,装饰边框与被装饰物具有了相同的方法。从接口角度而言,装饰边框与被装饰物具有相同的方法也就意味着它们具有一致性。且装饰边框类中有一个Display类型的字段来表示被装饰物。但是这个字段所表示的被装饰物并不局限于StringDisplay的实例,也可能是其他的装饰边框(也就是Border类的子类的实例)。且那个边框中也会有一个display的字段。
public abstract class Border extends Display{protected Display display; // 表示被装饰物protected Border(Display display) {// 在生成实例时通过参数指定被装饰物this.display = display;}}
SideBorder类:
SideBorder类是一种具体的装饰边框,是Border类的子类。Sideborder类用指定的字符装饰字符串的左右两侧。
public class SideBorder extends Border{private char borderChar;protected SideBorder(Display display,char borderChar) {super(display);this.borderChar=borderChar;}@Overridepublic int getColumns() {return 2+ display.getColumns();}@Overridepublic int getRows() {return display.getRows();}@Overridepublic String getRowText(int row) {return borderChar+ display.getRowText(row)+borderChar;}}
FullBorder类:
和SideBorder不同的是,FullBorder不能指定字符,而是固定字符。
public class FullBorder extends Border{public FullBorder(Display display) {super(display);}@Overridepublic int getColumns() {return 2+display.getColumns();}@Overridepublic int getRows() {return 2+ display.getRows();}@Overridepublic 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 stringBuffer = new StringBuffer();for (int i = 0; i < count; i++) {stringBuffer.append(ch);}return stringBuffer.toString();}}
Main类:
使用装饰者模式的姿势也非常重要。
public class Main {public static void main(String[] args) {StringDisplay b1 = new StringDisplay("Hello World");SideBorder b2 = new SideBorder(b1, '#');FullBorder b3 = new FullBorder(b2);b1.show();b2.show();b3.show();SideBorder b4 = new SideBorder(new FullBorder(new FullBorder(new SideBorder(new FullBorder(new StringDisplay("你好世界")), '#'))), '*');b4.show();}}
Decorator模式中的登场角色:
Component:
增加功能时的核心角色。装饰前的蛋糕就是Component角色,定义了蛋糕的接口。
ConcreteComponent:
Decorator(装饰物):
该角色具有与Component角色相同的接口,在它内部保存了被装饰对象-Component角色。Decorator与自己装饰的对象一一绑定。
ConcreteDecorator(具体的装饰物):
拓展思路:
接口的透明性:
在Decorator模式中,装饰边框与被装饰物具有一致性。具体而言,表示装饰边框的Border是表示被装饰物的DisPlay类的子类,这就体现了他们的一致性。也就是说,Border类与表示被装饰物的Display类具有相同的接口。
这样,即便被装饰物被边框装饰起来了,接口也不会被隐藏起来。其他类依然可以调用方法。这就是接口的透明性。
得益于接口的透明性,Decorator模式中形成了类似于Composite模式中的递归模式。也就是说,装饰边框里面的“被装饰物”实际上又是别的物体的“装饰边框”。但其实目的不同,Decorator模式的主要目的是通过添加装饰物来增加对象的功能。
在不改变被装饰物的前提下增加功能:
在Decorator模式中,装饰边框与被装饰物具有相同的接口。虽然API是相同的,但是越装饰,功能则越多。此时,完全不需要对被装饰的类做任何修改,这样,就实现了不修改被装饰的类即可增加功能。
可以动态地增加功能:
Decorator模式中用到了委托,它使类之间形成了弱关联关系。因此,不用改变框架代码,就可以生成一个与其他对象具有不同关系的新对象。
只需要一些装饰物即可添加许多功能:
使用Decorator模式可以为程序添加许多功能。只要准备一些装饰边框,即使这些装饰边框都只具有非常简单的功能,也可以将它们自由组合成为新的对象。
各种香料组合来满足各位顾客的不同需求。Decorator模式就是可以应对这种多功能对象的需求的一种模式。
Java.io包和Decorator模式:
FileReader fileReader = new FileReader("a.txt");BufferedReader bufferedReader = new BufferedReader(new FileReader("a.txt"));LineNumberReader lineNumberReader = new LineNumberReader(new BufferedReader(new FileReader("a.txt")));LineNumberReader lineNumberReader1 = new LineNumberReader(new FileReader("a.txt"));
相关的设计模式:
Adapter模式:
Decorator模式可以在不改变被修饰物的接口的前提下,为被装饰物添加边框。
Adapter模式用于适配两个不同的接口。
Stragety模式:
Decorator模式可以像改变被装饰物的边框或是为被装饰物添加多重边框那样,来增加类的功能。
Stragety模式通过整体地替换算法来改变类的功能。
