装饰器设计模式

!!! note 目录

装饰器设计模式

一、为何提出装饰器设计模式?

在我们完成业务之后可能要求添加新的行为,如果直接在类本身改的话不符合OCP原则。还有一种方式就是对我的类再实现子类,在子类中实现要添加的新功能,但是会引起类爆炸。

1
2
3
4
package com.camellia.io.decorator.package01;
public interface Flyable {
void fly();
}
1
2
3
4
5
6
7
package com.camellia.io.decorator.package01;
public class Cat implements Flyable{
@Override
public void fly() {
System.out.println("Cat fly");
}
}
1
2
3
4
5
6
7
package com.camellia.io.decorator.package01;
public class Bird implements Flyable{
@Override
public void fly() {
System.out.println("Bird fly!");
}
}
1
2
3
4
5
6
7
8
9
10
package com.camellia.io.decorator.package01;

public class CatSub extends Cat{
@Override
public void fly() {
super.fly();
System.out.println("Cat 新方法");
}
}

1
2
3
4
5
6
7
8
package com.camellia.io.decorator.package01;
public class BirdSub extends Bird{
@Override
public void fly() {
super.fly();
System.out.println("Bird 新方法");
}
}

耦合度高,且会发生类爆炸。

二、装饰器设计模式

2.1 主要目标

装饰器设计模式的主要目标是在不修改现有代码的情况下,动态地为对象添加新功能。这种方式可以保持代码的松耦合,同时提高系统的灵活性和可维护性。

2.1 重要角色

  1. 装饰者(Decorator)

    • 为对象动态添加新功能。
    • 持有被装饰对象的引用。
  2. 被装饰者(Component)

    • 被动态添加新功能的对象。
    • 提供一个接口或抽象类,装饰者和被装饰者都实现或继承这个接口或抽象类。

2.3 装饰器模式要求

在装饰器设计模式中,装饰者和被装饰者必须实现相同的接口或继承相同的抽象类。这样做可以确保装饰者能够替代被装饰者,而客户端代码无需知道它们的区别。

2.4 为什么装饰者和被装饰者要实现同一个接口

当装饰者和被装饰者实现相同的接口时,客户端可以透明地使用装饰者,就像在使用被装饰者一样。这使得我们可以在不修改客户端代码的情况下,动态地为对象添加功能。

*2.5 装饰者包含被装饰者的引用

装饰者持有被装饰者的引用,这样可以在运行时动态地组合多个装饰者,为对象添加不同的功能。这种设计方式(”A has a B”)比继承(”is a”)更灵活,也降低了系统的耦合度。

三、示例代码(初步)

1
2
3
4
5
package com.camellia.io.decorator.package02;

public interface Flyable {
void fly();
}
1
2
3
4
5
6
7
8
9
package com.camellia.io.decorator.package02;

// 被装饰者
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird fly!");
}
}
1
2
3
4
5
6
7
8
9
10
package com.camellia.io.decorator.package02;

// 被装饰者
public class Cat implements Flyable {
@Override
public void fly() {
System.out.println("Cat fly!");
}
}

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
38
39
40
41
42
43
44
45
46
47
48
package com.camellia.io.decorator.package02;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* LogDecorator 类是一个具体的装饰者,用于在 Flyable 对象的飞行过程中添加日志功能。
* 它在飞行前后记录日志信息。
*/
public class LogDecorator implements Flyable {

// 持有被装饰者的引用
private Flyable flyable;

/**
* 构造函数,接受一个 Flyable 对象。
* 通过组合的方式,包含被装饰者的引用。
*
* @param flyable 被装饰的对象,实现了 Flyable 接口
*/
public LogDecorator(Flyable flyable) {
this.flyable = flyable;
}

/**
* 重写 fly 方法,在飞行前后记录日志。
* 在调用被装饰者的 fly 方法之前,记录起飞前的日志信息;
* 在调用被装饰者的 fly 方法之后,记录降落后的日志信息。
*/
@Override
public void fly() {
// 获取当前时间
Date now = new Date();
// 创建日期格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
// 打印起飞前的日志信息
System.out.println(sdf.format(now) + ": 起飞前的准备");

// 调用被装饰者的 fly 方法,执行飞行操作
flyable.fly();

// 获取当前时间
now = new Date();
// 打印降落后的日志信息
System.out.println(sdf.format(now) + ": 安全降落");
}
}

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
38
39
40
41
42
package com.camellia.io.decorator.package02;

/**
* TimerDecorator 类是一个具体的装饰者,用于在 Flyable 对象的飞行过程中添加计时功能。
* 它在飞行前后记录时间,并计算飞行耗时。
*/
public class TimerDecorator implements Flyable {

// 持有被装饰者的引用
private Flyable flyable;

/**
* 构造函数,接受一个 Flyable 对象。
* 通过组合的方式,包含被装饰者的引用。
*
* @param flyable 被装饰的对象,实现了 Flyable 接口
*/
public TimerDecorator(Flyable flyable) {
this.flyable = flyable;
}

/**
* 重写 fly 方法,在飞行前后记录时间,并计算飞行耗时。
* 在调用被装饰者的 fly 方法之前,记录起始时间;
* 在调用被装饰者的 fly 方法之后,记录结束时间,并计算和打印飞行耗时。
*/
@Override
public void fly() {
// 记录起始时间(前增强)
long begin = System.currentTimeMillis();

// 调用被装饰者的 fly 方法,执行飞行操作
flyable.fly();

// 记录结束时间(后增强)
long end = System.currentTimeMillis();

// 打印飞行耗时
System.out.println("耗时 " + (end - begin) + " 毫秒");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.camellia.io.decorator.package02;

public class ZMain {
public static void main(String[] args) {
// 使用装饰器为 Cat 对象添加计时功能
Flyable flyable1 = new TimerDecorator(new Cat());
flyable1.fly();

// 使用装饰器为 Bird 对象添加日志功能
Flyable flyable2 = new LogDecorator(new Bird());
flyable2.fly();
}
}

这样虽然满足需求但是一旦装饰者多了不利于维护,我们可以提取公共的部分放到一个抽象父类中。

三、示例代码

1
2
3
4
5
6
package com.camellia.io.decorator.package03;

public interface Flyable {
void fly();
}

1
2
3
4
5
6
7
8
9
10
package com.camellia.io.decorator.package03;

// 被装饰者
public class Bird implements Flyable{
@Override
public void fly() {
System.out.println("Bird fly!");
}
}

1
2
3
4
5
6
7
8
9
10
package com.camellia.io.decorator.package03;

// 被装饰者
public class Cat implements Flyable{
@Override
public void fly() {
System.out.println("Cat fly!");
}
}

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
package com.camellia.io.decorator.package03;

/**
* 所有的装饰者应该有一个共同的父类。这个父类通常是一个抽象类。
* FlyableDecorator 作为所有装饰者的基类,包含被装饰者的引用。
*/
public abstract class FlyableDecorator implements Flyable {
private Flyable flyable;

/**
* 构造函数,接受一个Flyable对象。
* 通过组合的方式,包含被装饰者的引用。
*
* @param flyable 被装饰的对象,实现了Flyable接口
*/
public FlyableDecorator(Flyable flyable) {
this.flyable = flyable;
}

/**
* 实现Flyable接口的fly方法。
* 调用被装饰者的fly方法,以实现基本的飞行功能。
*/
@Override
public void fly() {
flyable.fly();
}
}

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
38
39
package com.camellia.io.decorator.package03;

/**
* TimerDecorator 类是一个具体的装饰者,用于在 Flyable 对象的飞行过程中添加计时功能。
* 它在飞行前后记录时间,并计算飞行耗时。
*/
public class TimerDecorator extends FlyableDecorator {

/**
* 构造函数,接受一个 Flyable 对象。
* 通过组合的方式,包含被装饰者的引用。
*
* @param flyable 被装饰的对象,实现了 Flyable 接口
*/
public TimerDecorator(Flyable flyable) {
super(flyable);
}

/**
* 重写 fly 方法,在飞行前后记录时间,并计算飞行耗时。
* 在调用被装饰者的 fly 方法之前,记录起始时间;
* 在调用被装饰者的 fly 方法之后,记录结束时间,并计算和打印飞行耗时。
*/
@Override
public void fly() {
// 记录起始时间(前增强)
long begin = System.currentTimeMillis();

// 调用被装饰者的 fly 方法,执行飞行操作
super.fly();

// 记录结束时间(后增强)
long end = System.currentTimeMillis();

// 打印飞行耗时
System.out.println("耗时 " + (end - begin) + " 毫秒");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.camellia.io.decorator.package03;

public class ZMain {
public static void main(String[] args) {
// 使用装饰器为 Cat 对象添加计时功能
Flyable flyable1 = new TimerDecorator(new Cat());
flyable1.fly();

// 使用装饰器为 Bird 对象添加日志功能
Flyable flyable2 = new LogDecorator(new Bird());
flyable2.fly();
}
}

五、类图