1. 程序中的对象与蛋糕非常相似。首先有一个相当于蛋糕的对象,然后像不断地装饰蛋糕一样不断地对其增加功能,它就变成了使用目的更加明确的对象。<br />像这样不断地为对象添加装饰的设计模式被称为Decorator模式。

示例程序:

名字 说明
Display 用于显示字符串的抽象类
StringDisplay 用于显示单行字符串的类
Border 用于显示装饰边框的抽象类
SideBorder 用于只显示左右边框的类
FullBorder 用于显示上下左右边框的类
Main 测试程序行为的类

Display:

Display类是可以显示多行字符串的抽象类。
其中show是显示所有行的字符串的方法。在show方法内部,程序会调用getRows方法获取行数,调用get
RowText获取该行需要显示的字符串,然后通过for循环间所有的字符串显示出来。show方法使用了getRows和getRowText等抽象方法,属于Tempate Method模式。

  1. public abstract class Display {
  2. public abstract int getColumns();// 获取横向字符数
  3. public abstract int getRows(); // 获取纵向行数
  4. public abstract String getRowText(int row);// 获取第Row行的字符串
  5. public final void show(){ //全部显示
  6. for (int i = 0; i < getRows(); i++) {
  7. System.out.println(getRowText(i));
  8. }
  9. }
  10. }

StringDisplay类:

作为Display的子类,负责实现display中定义的抽象接口。程序功能是只显示单行字符串,所以只需要一个String的变量存储该行字符串即可。

  1. public class StringDisplay extends Display{
  2. private String string;
  3. public StringDisplay(String string) {
  4. this.string = string;
  5. }
  6. @Override
  7. public int getColumns() {
  8. return string.getBytes().length;
  9. }
  10. @Override
  11. public int getRows() {
  12. return 1;
  13. }
  14. @Override
  15. public String getRowText(int row) {
  16. if (row ==0) {
  17. return string;
  18. }else {
  19. return null;
  20. }
  21. }
  22. }

Border类:

Border类是装饰边框的抽象类。虽然它所表示的是装饰边框,但它也是Display的子类。
原因是,通过继承,装饰边框与被装饰物具有了相同的方法。从接口角度而言,装饰边框与被装饰物具有相同的方法也就意味着它们具有一致性。且装饰边框类中有一个Display类型的字段来表示被装饰物。但是这个字段所表示的被装饰物并不局限于StringDisplay的实例,也可能是其他的装饰边框(也就是Border类的子类的实例)。且那个边框中也会有一个display的字段。

  1. public abstract class Border extends Display{
  2. protected Display display; // 表示被装饰物
  3. protected Border(Display display) {// 在生成实例时通过参数指定被装饰物
  4. this.display = display;
  5. }
  6. }

SideBorder类:

SideBorder类是一种具体的装饰边框,是Border类的子类。Sideborder类用指定的字符装饰字符串的左右两侧。

  1. public class SideBorder extends Border{
  2. private char borderChar;
  3. protected SideBorder(Display display,char borderChar) {
  4. super(display);
  5. this.borderChar=borderChar;
  6. }
  7. @Override
  8. public int getColumns() {
  9. return 2+ display.getColumns();
  10. }
  11. @Override
  12. public int getRows() {
  13. return display.getRows();
  14. }
  15. @Override
  16. public String getRowText(int row) {
  17. return borderChar+ display.getRowText(row)+borderChar;
  18. }
  19. }

FullBorder类:

和SideBorder不同的是,FullBorder不能指定字符,而是固定字符。

  1. public class FullBorder extends Border{
  2. public FullBorder(Display display) {
  3. super(display);
  4. }
  5. @Override
  6. public int getColumns() {
  7. return 2+display.getColumns();
  8. }
  9. @Override
  10. public int getRows() {
  11. return 2+ display.getRows();
  12. }
  13. @Override
  14. public String getRowText(int row) {
  15. if (row==0) {
  16. return "+"+makeLine('-', display.getColumns())+'+';
  17. }else if(row==display.getRows()+1){
  18. return "+"+makeLine('-', display.getColumns())+'+';
  19. }else{
  20. return "|"+display.getRowText(row-1)+"|";
  21. }
  22. }
  23. private String makeLine(char ch,int count){
  24. StringBuffer stringBuffer = new StringBuffer();
  25. for (int i = 0; i < count; i++) {
  26. stringBuffer.append(ch);
  27. }
  28. return stringBuffer.toString();
  29. }
  30. }

Main类:

使用装饰者模式的姿势也非常重要。

  1. public class Main {
  2. public static void main(String[] args) {
  3. StringDisplay b1 = new StringDisplay("Hello World");
  4. SideBorder b2 = new SideBorder(b1, '#');
  5. FullBorder b3 = new FullBorder(b2);
  6. b1.show();
  7. b2.show();
  8. b3.show();
  9. SideBorder b4 = new SideBorder(new FullBorder(new FullBorder(new SideBorder(new FullBorder(new StringDisplay("你好世界")), '#'))), '*');
  10. b4.show();
  11. }
  12. }

Decorator模式中的登场角色:

Component:

增加功能时的核心角色。装饰前的蛋糕就是Component角色,定义了蛋糕的接口。

ConcreteComponent:

该角色是实现了Component角色定义的接口的具体蛋糕。

Decorator(装饰物):

该角色具有与Component角色相同的接口,在它内部保存了被装饰对象-Component角色。Decorator与自己装饰的对象一一绑定。

ConcreteDecorator(具体的装饰物):

具体的Decorator角色。

拓展思路:

接口的透明性:

在Decorator模式中,装饰边框与被装饰物具有一致性。具体而言,表示装饰边框的Border是表示被装饰物的DisPlay类的子类,这就体现了他们的一致性。也就是说,Border类与表示被装饰物的Display类具有相同的接口。
这样,即便被装饰物被边框装饰起来了,接口也不会被隐藏起来。其他类依然可以调用方法。这就是接口的透明性。
得益于接口的透明性,Decorator模式中形成了类似于Composite模式中的递归模式。也就是说,装饰边框里面的“被装饰物”实际上又是别的物体的“装饰边框”。但其实目的不同,Decorator模式的主要目的是通过添加装饰物来增加对象的功能。

在不改变被装饰物的前提下增加功能:

在Decorator模式中,装饰边框与被装饰物具有相同的接口。虽然API是相同的,但是越装饰,功能则越多。此时,完全不需要对被装饰的类做任何修改,这样,就实现了不修改被装饰的类即可增加功能。

可以动态地增加功能:

Decorator模式中用到了委托,它使类之间形成了弱关联关系。因此,不用改变框架代码,就可以生成一个与其他对象具有不同关系的新对象。

只需要一些装饰物即可添加许多功能:

使用Decorator模式可以为程序添加许多功能。只要准备一些装饰边框,即使这些装饰边框都只具有非常简单的功能,也可以将它们自由组合成为新的对象。
各种香料组合来满足各位顾客的不同需求。Decorator模式就是可以应对这种多功能对象的需求的一种模式。

Java.io包和Decorator模式:

  1. FileReader fileReader = new FileReader("a.txt");
  2. BufferedReader bufferedReader = new BufferedReader(new FileReader("a.txt"));
  3. LineNumberReader lineNumberReader = new LineNumberReader(
  4. new BufferedReader(
  5. new FileReader("a.txt")
  6. )
  7. );
  8. LineNumberReader lineNumberReader1 = new LineNumberReader(new FileReader("a.txt"));

可以看出,java.io中使用了Decorator模式。

相关的设计模式:

Adapter模式:

Decorator模式可以在不改变被修饰物的接口的前提下,为被装饰物添加边框。
Adapter模式用于适配两个不同的接口。

Stragety模式:

Decorator模式可以像改变被装饰物的边框或是为被装饰物添加多重边框那样,来增加类的功能。
Stragety模式通过整体地替换算法来改变类的功能。