架构师内功心法,有重构项目经验必备的装饰者模式详解

Posted 1994july

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了架构师内功心法,有重构项目经验必备的装饰者模式详解相关的知识,希望对你有一定的参考价值。

一、装饰者模式的应用场景

在我们的生活中比如给煎饼加个鸡蛋,给蛋糕加上一些水果,给房子装修等。为对象扩展一些额外对象的职责。装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)。 技术图片 技术图片 技术图片

装饰者模式使用于以下几种场景:

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

1.1 ?? 早餐吃煎饼的装饰者模式

在北京早上通勤上班的同学们,都有吃过路边摊的煎饼吧。买煎饼的时候可以让他给你加个鸡蛋,也可以加香肠,还可以加辣条等。下面我们用代码来实现下这个生活场景的案例,首先创建一个煎饼 BatterCake 类:

public class BatterCake {

    protected String getMsg() {
        return "煎饼";
    }

    protected BigDecimal getPrice() {
        return new BigDecimal(6.00);
    }

}

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

public class BatterCakeWithEgg extends BatterCake {

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

    protected BigDecimal getPrice() {
        return new BigDecimal(6.00).add(new BigDecimal(1.00));
    }

}

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

public class BatterCakeWithEggAndSausage extends BatterCakeWithEgg {

    @Override
    protected String getMsg() {
        return super.getMsg() + "又加了1根香肠";
    }

    @Override
    protected BigDecimal getPrice() {
        return super.getPrice().add(new BigDecimal(2.00));
    }
}

测试main方法:

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

运行结果:

技术图片

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

public abstract class Battercake {

    protected abstract String getMsg();

    protected abstract BigDecimal getPrice();

}

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

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

    @Override
    protected BigDecimal getPrice() {
        return new BigDecimal(6.00);
    }
}

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

public class BattercakeDecorator extends BaseBattercake {

    private Battercake battercake;

    public BattercakeDecorator(Battercake battercake) {
        this.battercake = battercake;
    }

    @Override
    protected String getMsg() {
        return this.battercake.getMsg();
    }

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

创建鸡蛋装饰者 EggDecorator 类:

public class EggDecorator extends BattercakeDecorator {
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }

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

    @Override
    protected BigDecimal getPrice() {
        return super.getPrice().add(new BigDecimal(1.00));
    }
}

创建香肠装饰者 SausageDecorator 类:

public class SausageDecorator extends BattercakeDecorator {
    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }

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

    @Override
    protected BigDecimal getPrice() {
        return super.getPrice().add(new BigDecimal(2.00));
    }
}

测试代码:

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().doubleValue()); }

运行结果:

技术图片

最后看一下类图:

技术图片

二、装饰者模式在源码中的体现

2.1 IO相关的类

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

2.2 Spring中的装饰者模式

在 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;
    }
    public Cache getTargetCache() {
    return this.targetCache;
    }
    ...
}

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

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

    public final Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        return Flux.from(body).reduce(0, (current, buffer) -> {
            int next = current + buffer.readableByteCount();
            DataBufferUtils.release(buffer);
            return next;
        }).doOnNext((count) -> {
            this.getHeaders().setContentLength((long)count);
        }).then();
    }

    public final Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
        return this.setComplete();
    }
}

三、 装饰者模式的优缺点

优点:

  • 装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用;

  • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果;

  • 装饰者完全遵守开闭原则。

缺点:

  • 会出现更多的代码,更多的类,增加程序复杂性;

  • 动态装饰时,多层装饰时会更复杂。

来源:迅闻网

以上是关于架构师内功心法,有重构项目经验必备的装饰者模式详解的主要内容,如果未能解决你的问题,请参考以下文章

架构师内功心法,注重方法调用顺序的建造者模式详解

架构师内功心法,注重方法调用顺序的建造者模式详解

架构师内功心法,只是单纯听说过的原型模式详解

架构师内功心法,只是单纯听说过的原型模式详解

架构师内功心法,属于游戏设计模式的策略模式详解

架构师内功心法,属于游戏设计模式的策略模式详解