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、 裝飾者模式的優缺點
優勢:
-
裝飾者是繼承的有力補充,比繼承靈活,不改變原有對象的狀況下動態地給一個對象擴展功能,即插即用;
-
經過使用不一樣裝飾類以及這些裝飾類的排列組合,能夠實現不一樣效果;
-
裝飾者徹底遵照開閉原則。
缺點:
-
會出現更多的代碼,更多的類,增長程序複雜性;
-
動態裝飾時,多層裝飾時會更復雜。