装饰器模式

装饰器模式

一、什么是装饰器模式?

装饰器(Decorator)模式:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式。

二、为什么要用装饰器模式?

2.1 装饰和代理

装饰器模式和代理模式,都可以用于增强原始类现有功能,但它们的使用还是有一点区别的:

  • 代理模式中,增加的功能是与原始类核心功能无关的
  • 装饰器模式中,增加的功能是与原始类核心功能相关的
  • 装饰器模式中,装饰器和原始类都是继承于同一个父类,因而可以实现嵌套装饰

2.2 装饰和继承

继承也可以实现功能增强,装饰和继承的区别在于:

  • 继承可能会导致类组合爆炸,类结构异常复杂,代码不易维护和扩展
  • 装饰器模式使用组合替代继承,而且继承于同一父类,代码易于扩展

三、怎么用装饰器模式?

3.1 模式的结构是怎么样的?

装饰器模式的主要角色包括:

  1. 抽象构件角色(Component):定义的抽象接口,用于规范被附加功能的对象
  2. 具体构件角色(ConcreteComponet):实现抽象构件,实现它自己的核心功能
  3. 抽象装饰角色(Decorator):定义的抽象装饰接口,继承于抽象构件,并包含具体构件的实例
  4. 具体装饰角色(ConcreteDecorator):实现抽象装饰接口,为具体构件对象添加额外的功能

装饰器结构

3.2 装饰器模式示例

示例程序的类结构图:

装饰器结构

抽象构件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Display {

public abstract int getColumns();

public abstract int getRows();

public abstract String getRowText(int row);

public final void show() {
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}

}

具体构件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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() {
return 1;
}

@Override
public String getRowText(int row) {
if (row == 0) {
return string;
} else {
return null;
}
}
}

抽象装饰:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class Border extends Display {

protected Display display;

protected Border(Display display) {
this.display = display;
}

@Override
public int getColumns() {
return display.getColumns();
}

@Override
public int getRows() {
return display.getRows();
}

@Override
public String getRowText(int row) {
return display.getRowText(row);
}
}

具体装饰:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SideBorder extends Border {

private char borderChar;

protected SideBorder(Display display, char ch) {
super(display);
this.borderChar = ch;
}

@Override
public int getColumns() {
// 左右两边加上装饰符后,长度需要加2
return 1 + display.getColumns() + 1;
}

@Override
public String getRowText(int row) {
// 给左右两边加上装饰符
return borderChar + display.getRowText(row) + borderChar;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class FullBorder extends Border {

protected FullBorder(Display display) {
super(display);
}

@Override
public int getColumns() {
return 1 + display.getColumns() + 1;
}

@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(char ch, int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(ch);
}
return sb.toString();
}
}

四、装饰器模式有什么优缺点?

优点:

  • 使用组合代替继承,可以灵活地扩展功能,即插即用
  • 不同装饰者的组合,可以实现不同的功能效果

缺点:

  • 使用装饰器模式会增加很多子类,使用起来可能会比较麻烦
  • 如果过度使用装饰器,可能会导致类嵌套过深,增加了程序的复杂性
作者

jiaduo

发布于

2022-01-15

更新于

2023-04-03

许可协议