架構師內功心法,有重構項目經驗必備的裝飾者模式詳解

1、裝飾者模式的應用場景

在咱們的生活中好比給煎餅加個雞蛋,給蛋糕加上一些水果,給房子裝修等。爲對象擴展一些額外對象的職責。裝飾者模式(Decorator Pattern)是指在不改變原有對象的基礎之上,提供了比繼承更有彈性的替代方案(擴展原有對象的功能)。 緩存

裝飾者模式使用於如下幾種場景:ide

  • 用於擴展一個類的功能或給一個類添加附加職責;
  • 動態的給一個對象添加功能,這些功能能夠動態的撤銷。

1.1 🌮 早餐吃煎餅的裝飾者模式

在北京早上通勤上班的同窗們,都有吃過路邊攤的煎餅吧。買煎餅的時候可讓他給你加個雞蛋,也能夠加香腸,還能夠加辣條等。下面咱們用代碼來實現下這個生活場景的案例,首先建立一個煎餅 BatterCake 類:測試

public class BatterCake {

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

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

}

再建立一個加雞蛋的煎餅 BatterCakeWithEgg 類:this

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 類:code

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

運行結果:blog

上面的程序運行結果沒有問題,若是須要加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 類:ci

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、裝飾者模式在源碼中的體現

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

3、 裝飾者模式的優缺點

優勢:

  • 裝飾者是繼承的有力補充,比繼承靈活,不改變原有對象的狀況下動態地給一個對象擴展功能,即插即用;

  • 經過使用不一樣裝飾類以及這些裝飾類的排列組合,能夠實現不一樣效果;

  • 裝飾者徹底遵照開閉原則。

缺點:

  • 會出現更多的代碼,更多的類,增長程序複雜性;

  • 動態裝飾時,多層裝飾時會更復雜。

相關文章
相關標籤/搜索