Design Patterns: Decorator 装饰器模式

装饰器模式

结构型模式

在不改变代码的情况下给一个对象增加功能,类似于继承中覆写方法,但是可以在运行时进行改变。用装饰器模式可以替代继承。

具体实现:

要被装饰的类叫做实体类ConcreteComponent,在这个实体类外面再包装一个类,叫做装饰类Decorator,这个装饰类和实体类一样继承自Component而且持有Component类型的引用decoratedComponent,这个引用其实是要被装饰的实体,也就是ConcreteComponent类型的。

功能扩展类ConcreteDecorator都继承自装饰类并覆写相应的方法。在这些功能扩展类拥有从装饰类里继承来的decoratedComponent引用。在每个功能扩展类看来,这个引用都是最初那个实体(但是其实可能是已经被层层装饰过了),然后在每个功能扩展类里对要装饰的方法再包装一层。

所以装饰器模式实现复用和扩展依靠的就是那个关键的decoratedComponent引用,在添加功能时不是通过层层继承,而是通过组合的方式。

看具体的例子会比较容易理解。

一个简单的例子

有一个房子,里面空空如也,现在要往房子里添加各种家具来装修这个房子。为了直观理解,先上运行的测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 一个空教室
House classroom = new Classroom();
System.out.println("==== Original ====");
classroom.show();
// 空教室加一把椅子
House classroomWithChair = new Chair(classroom);
System.out.println("==== Classroom with a chair ====");
classroomWithChair.show();
// 空教室加一个桌子
House classroomWithDesk = new Desk(classroom);
System.out.println("==== Classroom with a desk ====");
classroomWithDesk.show();
// 在加了椅子的基础上加桌子
House classroomWithChairAndDesk = new Desk(classroomWithChair);
System.out.println("==== Classroom with a chair and a desk ====");
classroomWithChairAndDesk.show();

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
==== Original ====
A classroom.
==== Classroom with a chair ====
A classroom.
There is a chair
==== Classroom with a desk ====
A classroom.
There is a desk
==== Classroom with a chair and a desk ====
A classroom.
There is a chair
There is a desk

可以看到如果要给教室添加家具,只需要在外面嵌套一个类,而且已经被嵌套过的对象还能再次嵌套。

这里的House对应ComponentClassroom对应ConcreteComponentChairDesk都对应ConcreteComponent

如果最后只需要一个教室,可以重复使用同一个引用

1
2
3
4
5
6
7
8
9
10
11
House classroom = new Classroom();
System.out.println("==== Original ====");
classroom.show();

classroom = new Chair(classroom);
System.out.println("==== Add a chair ====");
classroom.show();

classroom = new Desk(classroom);
System.out.println("==== Add a desk ====");
classroom.show();

实现

一个共同的基类House

1
2
3
4
5
public abstract class House{
protected String name = "House";

public abstract void show();
}

要被装饰的类Classroom,要被装饰的方法是show

1
2
3
4
5
6
7
8
9
10
public class Classroom extends House{
public Classroom() {
name = "Classroom";
}

@Override
public void show() {
System.out.println("A classroom.");
}
}

装饰器基类HouseDecorator

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class HouseDecorator extends House{
protected House house;

public HouseDecorator(House house) {
this.house = house;
}

@Override
public void show() {
house.show();
}
}

具体的装饰器ChairDesk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Chair extends HouseDecorator{
public Chair(House house) {
super(house);
}

@Override
public void show() {
house.show();
System.out.println("There is a chair");
}
}

public class Desk extends HouseDecorator{
public Desk(House house) {
super(house);
}

@Override
public void show() {
house.show();
System.out.println("There is a desk");
}
}

简化

  1. 如果要被装饰的类只有一个,比如这里只有一个Classroom,那么就可以把共同的基类省掉,让装饰器也直接继承自这个类,这里就是让HouseDecorator继承自Classroom
  2. 如果具体的装饰器只有一个,比如只有Chair,那么就可以把装饰器类省掉,直接让具体的装饰器继承自共同的基类,也就是让Chair继承自House