联系我

设计模式之装饰者模式

2020.06.24

定义

装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

适用场景

  • 用于扩展一个类的功能或给一个类添加附加职责。
  • 动态的给一个对象添加功能,这些功能可以再动态的撤销。

生活场景实例

这里以一个买煎饼的例子为例来讲解一下装饰者模式,首先创建一个煎饼 Battercake 类:

public class Battercake {
    protected String getMsg(){
        return "煎饼";
    } 
    public int getPrice(){
        return 5;
    }
}

创建一个加鸡蛋的煎饼 BattercakeWithEgg 类:

public class BattercakeWithEgg extends Battercake {
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1 个鸡蛋";
    }
    //加一个鸡蛋加 1 块钱
    @Override
    public int getPrice() {
        return super.getPrice() + 1;
    }
}

再创建一个既加鸡蛋又加香肠的 BattercakeWithEggAndSausage 类:

    @Override
    protected String getMsg() {
        return super.getMsg() + "+1 根香肠";
    }
    
    @Override
    //加一个香肠加 2 块钱
    public int getPrice() {
        return super.getPrice() + 2;
    }
}

客户端测试代码:

public class BattercakeTest {
    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getMsg() + ",总价格: " + battercake.getPrice());
        Battercake battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg() + ",总价格: " + battercakeWithEgg.getPrice());
        Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格: " +
                battercakeWithEggAndSausage.getPrice());
    }
}

运行结果:

煎饼,总价格: 5
煎饼+1 个鸡蛋,总价格: 6
煎饼+1 个鸡蛋+1 根香肠,总价格: 8

运行结果没有问题。但是,如果用户需要一个加 2 个鸡蛋加 1 根香肠的煎饼,那么用我们现在的类结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。如果需求再变,一直加定制显然是不科学的。那么下面我们就用装饰者模式来解决上面的问题。首先创建一个建煎饼的抽象 Battercake 类:

public abstract class Battercake {
    protected abstract String getMsg();
    protected abstract int getPrice();
}

然后创建一个基本的煎饼(或者叫基础套餐)BaseBattercake:

public class BaseBattercake extends Battercake {
    @Override
    protected String getMsg() {
        return "煎饼";
    }

    @Override
    protected int getPrice() {
        return 5;
    }
}

再创建一个扩展套餐的抽象装饰者 BattercakeDecotator 类:

public abstract class BattercakeDecotator extends Battercake {
    //静态代理, 委派
    private Battercake battercake;
    public BattercakeDecotator(Battercake battercake) {
        this.battercake = battercake;
    }
    protected abstract void doSomething();
    @Override
    protected String getMsg() {
        return this.battercake.getMsg();
    }

    @Override
    protected int getPrice() {
        return this.battercake.getPrice();
    }
}

创建鸡蛋装饰者 EggDecorator 类:

public class EggDecorator extends BattercakeDecotator {
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }
    @Override
    protected void doSomething() {

    }
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1 个鸡蛋";
    }
    @Override
    protected int getPrice() {
        return super.getPrice() + 1;
    }
}

创建香肠装饰者 SausageDecorator 类:

public class SausageDecorator extends BattercakeDecotator {
    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }
    @Override
    protected void doSomething() {

    }
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1 根香肠";
    } 
    
    @Override
    protected int getPrice() {
        return super.getPrice() + 2;
    }
}

编写客户端测试代码:

public class BattercakeTest {
    public static void main(String[] args) {
        Battercake battercake;
        //路边摊买一个煎饼
        battercake = new BaseBattercake();
        //煎饼有点小, 想再加一个鸡蛋
        battercake = new EggDecorator(battercake);
        //再加一个鸡蛋
        battercake = new EggDecorator(battercake);
        //很饿, 再加根香肠
        battercake = new SausageDecorator(battercake);
        
        System.out.println(battercake.getMsg() + ",总价: " + battercake.getPrice());

    }
}

运行结果:

煎饼+1 个鸡蛋+1 个鸡蛋+1 根香肠,总价: 9

装饰者模式在源码中的应用

装饰器模式在源码中也应用得非常多,在 JDK 中体现最明显的类就是 IO 相关的类,如BufferedReader、InputStream、OutputStream,看一下常用的 InputStream 的类结构图

在 Spring 中的 TransactionAwareCacheDecorator 类我们也可以来尝试理解一下,这个类主要是用来处理事务缓存的,来看一下代码:

public class TransactionAwareCacheDecorator implements Cache {
    private final Cache targetCache;

    public TransactionAwareCacheDecorator(Cache targetCache) {
        Assert.notNull(targetCache, "Target Cache must not be null");
        this.targetCache = targetCache;
    }
    //....
}

T
ransactionAwareCacheDecorator 就是对 Cache 的一个包装。再来看一个 MVC 中的装饰者模式 HttpHeadResponseDecorator 类:

public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator {
    public HttpHeadResponseDecorator(ServerHttpResponse delegate) {
    	super(delegate);
    }
    ...
}

装饰者模式的优缺点

优点:
1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
3、装饰者完全遵守开闭原则。
缺点:
1、会出现更多的代码,更多的类,增加程序复杂性。
2、动态装饰时,多层装饰时会更复杂。