装饰模式

概念

动态地给对象添加一些额外的职责。就功能来说装饰模式比生成子类更为灵活。

在许多设计中,可能需要改进类的某个对象的功能,而不是该类创建的全部对象。例如,麻雀类的实例能连续飞行100米,如果用麻雀类创建5只麻雀,那么这5只麻雀都能连续飞行100米。假如想让其中一只麻雀能连续飞行150米,那我们应当怎么做呢?我们不想通过修改麻雀类的代码(也可能根本不允许修改)使得麻雀类创建的麻雀能够连续飞行150米。一种比较好的方法就是给麻雀装上智能电子翅膀。智能电子翅膀可以使得麻雀不使用自己的翅膀就能飞行50米。

装饰模式是动态地扩展一个对象的功能,而不需要改变原始类代码的一种成熟的设计模式。在装饰模式中,“具体组件”类和“具体装饰”类是该模式中最重要的两个角色。“具体组件”类的实例称作“被装饰者”,“具体装饰”类的实例称作“装饰者”。“具体装饰”类需要包含有“具体组件”类的一个实例的引用,以便装饰“被装饰者”。

例如,前面所述的麻雀类就是“具体组件”类,而一只麻雀就是“具体组件”类的一个实例,即是一个“被装饰者”,而安装了电子翅膀的麻雀就是“具体装饰”类的一个实例,即安装了电子翅膀的麻雀是麻雀的“装饰者”。

结构与使用

装饰模式的结构中包含四种角色。

  • 抽象组件(Component):抽象组件是一个抽象类。抽象组件定义了“被装饰者”需要进行“装饰”的方法。

  • 具体组件(ConcreteCommponent):具体组件是抽象组件的一个子类,具体组件的实例称作“被装饰者”。

  • 装饰(Decorator):装饰也是抽象组件的一个子类,但装饰还包括抽象组件申明的变量以保存“被装饰者”的引用。装饰可以使抽象类也可以是非抽象类,如果是非抽象类,它的实例称作“装饰者”。

  • 具体装饰(ConcreteDecorator):具体装饰是装饰的一个非抽象子类,具体装饰的实例称作“装饰者”。

UML类图

Decorator.jpg

结构的描述

1.抽象组件

本问题中,抽象组件规定了具体组件需要实现的方法。

1
2
3
public abstract class Bird {
public abstract int fly();
}

2.具体组件

具体组件是Sparrow类。

1
2
3
4
5
6
7
public class Sparrow extends Bird {
public final int DISTANCE = 100;
@Override
public int fly() {
return DISTANCE;
}
}

3.装饰

1
2
3
4
5
6
7
public abstract class Decorator extends Bird{
protected Bird bird;
public Decorator() {}
public Decorator(Bird bird){
this.bird = bird;
}
}

4.具体装饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SparrowDecorator extends Decorator {
public final int DISTANCE = 50;
public SparrowDecorator(Bird bird) {
super(bird);
}
@Override
public int fly() {
//委托被装饰者调用fly方法
return bird.fly() + eleFly();
}
//装饰者新添的方法
private int eleFly() {
return DISTANCE;
}
}

5.测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Application {
public void printFly(Bird bird){
System.out.println("这只鸟能飞行"+bird.fly()+"米。");
}
public static void main(String[] args) {
Application client = new Application();
//sparrow 只能飞行100米
Bird sparrow = new Sparrow();
//sparrowDecorator1 能飞150米
Bird sparrowDecorator1 = new SparrowDecorator(sparrow);
//sparrowDecorator2 能飞200米
Bird sparrowDecorator2 = new SparrowDecorator(sparrowDecorator1);
client.printFly(sparrowDecorator1);
client.printFly(sparrowDecorator2);
}
}

使用多个装饰者

由于装饰是抽象组件的一个子类,因此“装饰者”本身也可作为一个“被装饰者”,这意味着可以使用多个具体装饰类来装饰具体组件的实例。

比如,对于上面的小问题,假如我们不仅需要飞行150米、200米的鸟,而且需要飞行120米、170米、220米的鸟,这是我们只需再添加一个具体装饰即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SparrowDecoratorTwo extends Decorator{
public final int DISTANCE = 20;
public SparrowDecoratorTwo(Bird bird) {
super(bird);
}
@Override
public int fly() {
return bird.fly()+eleFly();
}
private int eleFly() {
return DISTANCE;
}
}

优点

  • 被装饰者和装饰者是松耦合关系,由于装饰(Decorator)仅仅依赖于抽象的组件(Component),因此具体装饰只知道他要装饰的对象是抽象组件某一个子类的实例,但不需要知道是哪一个具体子类。

  • 装饰模式满足“开-闭原则”。不必修改具体组件,就可以增加新的针对该具体组件的装饰。

  • 可以使用多个具体装饰来装饰具体组件的实例。

欢迎关注我的其它发布渠道