结构模式之装饰模式

Posted by Night Field's Blog on April 18, 2020

1 概述

装饰模式(Decorator Pattern),意在不改变原有对象的情况下,改变/增强它的方法。

2 装饰模式

在不违反开闭原则的前提下,要改变某个对象的行为,可以使用继承。然而继承不适用于类/方法被final修饰的情况,而且一般需要了解类内部的情况,违反了迪米特法则装饰模式体现了组合优先于继承的思想,通过组合的方式,“装饰”对象的功能,也能达到改变对象行为的目的。装饰模式的实现模式和代理模式很相似,都是实现目标对象的接口,然后持有目标类,调用目标类的方法。代理模式更偏向于对对象的控制,给对象添加与其无关的功能(打日志,权限校验等);而装饰模式更偏向于对对象的增强,即增强对象原有的方法,装饰后的对象还是原来的对象。

3 案例

举个例子。定义一个Car接口,看如何用装饰模式在不修改原有对象的基础上,对其方法进行增强:

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
public class Test {
    public static void main(String[] args) {
        Car basicCar = new BasicCar();
        // 装饰类
        Car sportsCar = new SportsCar(basicCar);
        basicCar.drive();
        System.out.println("top speed of basic car: " + basicCar.topSpeed());
        System.out.println("=============");
        sportsCar.drive();
        System.out.println("top speed of sports car: " + sportsCar.topSpeed());
    }
}

public interface Car {
    void drive();
    int topSpeed();
}

public class BasicCar implements Car {
    @Override
    public void drive() {
        System.out.println("Car is driving...");
    }
    @Override
    public int topSpeed() {
        return 120;
    }
}

// 装饰类
public class SportsCar implements Car {
    // 被装饰对象
    Car car;
    SportsCar(Car car) {
        this.car = car;
    }
    // 对drive()方法进行增强
    @Override
    public void drive() {
        System.out.println("Sports Car is build with high performance engine...");
        car.drive();
    }
    // 对topSpeed()方法进行增强
    @Override
    public int topSpeed() {
        return car.topSpeed() + 60;
    }
}

输出:

1
2
3
4
5
6
Car is driving...
top speed of basic car: 120
=============
Sports Car is build with high performance engine...
Car is driving...
top speed of sports car: 180

装饰类SportsCar实现了被装饰对象的接口,同时持有被装饰对象的实例,在调用被装饰对象的方法前后进行“装饰”。不影响原有类BasicCar的逻辑,符合开闭原则

JavaCollections工具类就使用了装饰模式synchronizedCollection(Collection<T> c)方法返回了一个装饰对象,对集合添加了同步处理,unmodifiableCollection(Collection<? extends T> c)方法返回了一个装饰对象,使得集合不能被修改。

4 总结

使用装饰模式可以在不改变原对象逻辑的情况下,实现对象方法的增强。

文中例子的github地址