装饰者模式讲解
這個(gè)模式比外觀模式稍微難了一點(diǎn),但也不是太難,也是可以理解的,那現(xiàn)在我們引入一個(gè)場(chǎng)景,下班晚了,樓下有賣煎餅的,然后買個(gè)煎餅,在單位的茶水間,加個(gè)蛋,有時(shí)候加個(gè)腸,那我們現(xiàn)在就想一下,商家在賣這個(gè)煎餅,加蛋的時(shí)候,加香腸的時(shí)候,這個(gè)價(jià)格是怎么計(jì)算的,首先我們?cè)谶@里創(chuàng)建一個(gè)裝飾者,我們先寫一個(gè)版本叫做v1
package com.learn.design.pattern.structural.decorator.v1;/*** 我們現(xiàn)在創(chuàng)建一個(gè)煎餅類* 他有兩個(gè)方法* * * @author Leon.Sun**/
public class Battercake {/*** 一個(gè)是獲取描述getDesc* * 我們把它寫成protected* 有的人喜歡在吃煎餅的時(shí)候加一個(gè)雞蛋* 再加一根香腸* * * @return*/protected String getDesc(){return "煎餅";}/*** 還有一個(gè)方法cost* 他的價(jià)格消費(fèi)多少* 假設(shè)這個(gè)煎餅8元* 現(xiàn)在商家創(chuàng)建一個(gè)煎餅類* 我在賣的時(shí)候可以賣一個(gè)煎餅* 我也可以賣加蛋的煎餅* 那我們?cè)賱?chuàng)建一個(gè)類* * * @return*/protected int cost(){return 8;}}
package com.learn.design.pattern.structural.decorator.v1;/*** 加蛋的煎餅* 繼承了Battercake* 這里我們重寫一下父類的方法* 因?yàn)闄?quán)限在子類中重寫* * * * @author Leon.Sun**/
public class BattercakeWithEgg extends Battercake {/*** 首先獲取父類獲得一個(gè)描述* 加上" 加一個(gè)雞蛋"* * * * */@Overridepublic String getDesc() {return super.getDesc()+" 加一個(gè)雞蛋";}/*** 價(jià)格也是* 煎餅賣8元* 加一個(gè)蛋加1塊* 這個(gè)時(shí)候我們看一下* 對(duì)于權(quán)限的一個(gè)限制* 里面也說一下* * * */@Overridepublic int cost() {return super.cost()+1;}
}
package com.learn.design.pattern.structural.decorator.v1;/*** 這個(gè)類呢是加一個(gè)雞蛋* 再加一根香腸* 我只需要讓他繼承加蛋的煎餅就可以了* 同理重寫* 這兩個(gè)方法* * * @author Leon.Sun**/
public class BattercakeWithEggSausage extends BattercakeWithEgg {/*** 他調(diào)用super之后呢* 他繼續(xù)掉super* 加上后邊" 加一根香腸"* * */@Overridepublic String getDesc() {return super.getDesc()+ " 加一根香腸";}/*** 香腸兩塊錢* 加2* 咱們寫一個(gè)測(cè)試類* * */@Overridepublic int cost() {return super.cost()+2;}
}
package com.learn.design.pattern.structural.decorator.v1;/*** * @author Leon.Sun**/
public class Test {public static void main(String[] args) {/*** 先new一個(gè)普通的* * */Battercake battercake = new Battercake();/*** 直接輸出銷售價(jià)格* 前面加上他的描述battercake.getDesc()* * */System.out.println(battercake.getDesc()+" 銷售價(jià)格:"+battercake.cost());/*** battercakeWithEgg* * */Battercake battercakeWithEgg = new BattercakeWithEgg();System.out.println(battercakeWithEgg.getDesc()+" 銷售價(jià)格:"+battercakeWithEgg.cost());/*** 加腸的也是同理* 這三個(gè)小伙伴都得到了滿足* 吃了一個(gè)煎餅* 吃了一個(gè)加雞蛋的煎餅* 也有一個(gè)加了雞蛋加了香腸的煎餅* 這個(gè)時(shí)候又來了一個(gè)小伙伴* 我要吃一個(gè)煎餅* 加兩個(gè)雞蛋* 加兩根香腸* 這個(gè)時(shí)候老板有點(diǎn)迷茫* 那我沒有加兩個(gè)雞蛋加兩根香腸的類* 那現(xiàn)在我這個(gè)系統(tǒng)算不出來* 這個(gè)應(yīng)該賣多少錢的* 這個(gè)沒有得到滿足* 商家也沒有掙到錢* 沒有賣出去* 因?yàn)橄到y(tǒng)不支持* 那這個(gè)UML非常簡(jiǎn)單* * */Battercake battercakeWithEggSausage = new BattercakeWithEggSausage();System.out.println(battercakeWithEggSausage.getDesc()+" 銷售價(jià)格:"+battercakeWithEggSausage.cost());}
}
就是一個(gè)單純的繼承,那這個(gè)擴(kuò)展性還是非常有限制的,我們想一下對(duì)于煎餅的組合呢,非常非常多,加一個(gè)雞蛋,加兩個(gè)雞蛋,加10個(gè)雞蛋的都有,那我們下邊的子類呢,不會(huì)發(fā)生類爆炸的情況嗎,如果按照現(xiàn)在的這種寫法來擴(kuò)展的話,肯定會(huì)發(fā)生的,那么這個(gè)時(shí)候我們就要考慮,怎么才能讓我們的代碼更優(yōu)雅,我們創(chuàng)建一個(gè)V2版本
我們現(xiàn)在要對(duì)煎餅進(jìn)行抽象,所謂的裝飾者模式,是說我們要有一個(gè)抽象的實(shí)體類,還要有確定的實(shí)體類,同時(shí)還需要有抽象的裝飾者,還要有確定的裝飾者,那在這個(gè)場(chǎng)景中,裝飾的主體是煎餅,裝飾者是雞蛋,還有香腸,在裝飾者模式中,四個(gè)角色在這個(gè)業(yè)務(wù)場(chǎng)景,是比較全的,抽象的食物,或者我們創(chuàng)建一個(gè)抽象的煎餅,因?yàn)檫@個(gè)煎餅具體的時(shí)候,怎么樣的還沒有確定,然后是實(shí)體的煎餅,緊接著是抽象的裝飾者,具體的裝飾者,香腸和雞蛋,那我們現(xiàn)在創(chuàng)建一個(gè)抽象的煎餅,用A開頭代表抽象類
現(xiàn)在我們來看一下整體的UML,是非常清晰的,首先最上層抽象煎餅,所以呢是一個(gè)接口,或者抽象類,但是這個(gè)抽象角色呢,并不是必須的,而BatterCake這個(gè)煎餅是具體的,要被裝飾的實(shí)體類,要被裝飾的對(duì)象也可以有多個(gè),那AbstractDecorator就是抽象的裝飾角色,而下邊的雞蛋和香腸,是具體的裝飾角色,負(fù)責(zé)給這個(gè)煎餅擴(kuò)展功能,那我們現(xiàn)在來run一下,新建Test
package com.learn.design.pattern.structural.decorator.v2;/*** 前邊加上abstract* 那這個(gè)方法我們直接拿過來* 前面要加上abstract* 因?yàn)樵诔橄箢惱锩? 這兩個(gè)方法要做成抽象方法* 那這個(gè)要被裝飾的實(shí)體呢* 我們也要聲明一個(gè)* 那就是實(shí)體煎餅* * * @author Leon.Sun**/
public abstract class ABattercake {protected abstract String getDesc();protected abstract int cost();}
package com.learn.design.pattern.structural.decorator.v2;/*** 他要繼承ABattercake* 實(shí)現(xiàn)方法* 現(xiàn)在重要的裝飾者就登場(chǎng)了* 首先我們讓抽象的裝飾者* 也繼承這個(gè)抽象的實(shí)體* * * @author Leon.Sun**/
public class Battercake extends ABattercake {@Overrideprotected String getDesc() {return "煎餅";}@Overrideprotected int cost() {return 8;}
}
package com.learn.design.pattern.structural.decorator.v2;/*** 創(chuàng)建AbstractDecorator這個(gè)類* 他繼承ABattercake這個(gè)抽象類* 然后實(shí)現(xiàn)里面的方法* 讓AbstractDecorator來繼承ABattercake* 很簡(jiǎn)單* 現(xiàn)在AbstractDecorator和實(shí)體的Battercake他們都是抽象類的子類* 那這個(gè)時(shí)候我們可以想象一下* 我如何讓這兩個(gè)子類建立關(guān)系呢* 我們可以通過它繼承的父類* 來達(dá)到這個(gè)目的* * AbstractDecorator這個(gè)類現(xiàn)在并不是抽象類* 但是也能完成擴(kuò)展的目的* 那為什么要加一層抽象的裝飾者呢* 很簡(jiǎn)單* 我們來寫一下* 現(xiàn)在把它改成抽象的類* 我們寫個(gè)方法* * * * @author Leon.Sun**/
public abstract class AbstractDecorator extends ABattercake {/*** 我們讓父類來聲明一個(gè)ABattercake* 這個(gè)抽象的煎餅放到這里了* * */private ABattercake aBattercake;/*** 然后就是他的構(gòu)造器* 通過構(gòu)造器把這個(gè)抽象的煎餅放進(jìn)來* 那現(xiàn)在我們看一下* 我們可以放抽象的煎餅進(jìn)來* 就可以放實(shí)體的煎餅進(jìn)來* 那這兩個(gè)也非常簡(jiǎn)單* 因?yàn)槲覀円呀?jīng)把煎餅放進(jìn)來了* 我們?cè)谡{(diào)用這個(gè)抽象的裝飾者的時(shí)候* 其實(shí)是把這個(gè)行為委托給抽象的煎餅來執(zhí)行的* 這個(gè)時(shí)候我們就要實(shí)現(xiàn)實(shí)體的裝飾者類* 我們有一個(gè)蛋裝飾者* * * @param aBattercake*/public AbstractDecorator(ABattercake aBattercake) {this.aBattercake = aBattercake;}/*** 我們想象一下* 如果作為抽象的裝飾者* 能夠保證子類實(shí)現(xiàn)某個(gè)方法的話* 那么這個(gè)抽象的裝飾者呢* 放到這里才會(huì)有意義* 所以這個(gè)類是不是抽象的類* 還要看具體的業(yè)務(wù)場(chǎng)景* 那在剛剛講的這個(gè)場(chǎng)景當(dāng)中呢* 他不是抽象的裝飾者* 也是OK的* 因?yàn)椴]有抽象方法* 新建的doSomething* 那如果我們創(chuàng)建了這個(gè)抽象方法* 那我們這個(gè)類應(yīng)該是抽象類* 這樣對(duì)于子類來說* 我們來看一下這個(gè)類* 是必須要實(shí)現(xiàn)doSomething方法的* * * */protected abstract void doSomething();@Overrideprotected String getDesc() {return this.aBattercake.getDesc();}@Overrideprotected int cost() {return this.aBattercake.cost();}
}
package com.learn.design.pattern.structural.decorator.v2;/*** EggDecorator這個(gè)類也是同理* * * @author Leon.Sun**/
public class EggDecorator extends AbstractDecorator {public EggDecorator(ABattercake aBattercake) {super(aBattercake);}/*** 那具體這個(gè)方法如何使用* 還是要看具體的業(yè)務(wù)場(chǎng)景* 例如老板在做煎餅* 加蛋的時(shí)候會(huì)有一個(gè)小動(dòng)作* 又或者加腸的時(shí)候* 也有一些特定的小動(dòng)作* 而這兩個(gè)小動(dòng)作呢* 分別屬于各自的裝飾者實(shí)現(xiàn)* 那這個(gè)時(shí)候?qū)τ趦蓚€(gè)實(shí)體的裝飾者的父類* 用抽象的裝飾者* 才會(huì)比較有意義* 具體最上層的裝飾者* 是否使用裝飾類* 要根據(jù)具體的業(yè)務(wù)場(chǎng)景* 這個(gè)呢并沒有硬規(guī)定* 那我們?cè)倏匆幌聢D* * */@Overrideprotected void doSomething() {}/*** 描述寫一下* " 加一個(gè)雞蛋"* * */@Overrideprotected String getDesc() {return super.getDesc()+" 加一個(gè)雞蛋";}/*** 一個(gè)雞蛋一元錢* * */@Overrideprotected int cost() {return super.cost()+1;}
}
package com.learn.design.pattern.structural.decorator.v2;/*** 首先用香腸的實(shí)體裝飾者類* 繼承抽象的裝飾者類* 然后實(shí)現(xiàn)這個(gè)默認(rèn)的構(gòu)造器* 那為什么要實(shí)現(xiàn)這個(gè)構(gòu)造器呢* 很簡(jiǎn)單* 這個(gè)父類已經(jīng)沒有無參構(gòu)造器了* 因?yàn)槲覀兟暶髁艘粋€(gè)有參構(gòu)造器* 然后重寫父類的這兩個(gè)方法* 很簡(jiǎn)單* * * @author Leon.Sun**/
public class SausageDecorator extends AbstractDecorator{public SausageDecorator(ABattercake aBattercake) {super(aBattercake);}@Overrideprotected void doSomething() {}/*** " 加一根香腸"* * */@Overrideprotected String getDesc() {return super.getDesc()+" 加一根香腸";}/*** 香腸賣兩元* * */@Overrideprotected int cost() {return super.cost()+2;}
}
package com.learn.design.pattern.structural.decorator.v2;/*** * @author Leon.Sun**/
public class Test {public static void main(String[] args) {/*** 我們聲明一個(gè)抽象的煎餅* 剛剛說的第四個(gè)小伙伴來買這個(gè)煎餅* 老板給他做* 程序也能跑* 怎么跑呢* 看一下* * */ABattercake aBattercake;/*** 首先我們要給一個(gè)煎餅* new一個(gè)Battercake* * */aBattercake = new Battercake();/*** 這個(gè)時(shí)候注意* 他要兩個(gè)雞蛋* 我們用雞蛋裝飾者來包裝一下這個(gè)煎餅* 然后呢* 再返回這個(gè)被包裝的這個(gè)煎餅* 因?yàn)檫@個(gè)裝飾者他繼承抽象的裝飾者* 而抽象的裝飾者呢* 看一下UML* 他繼承抽象的煎餅* 所以他的類型是可以被抽象的煎餅接收的* 同時(shí)我們注意UML里面的這個(gè)組合* 一個(gè)抽象的裝飾者里面有一個(gè)抽象的煎餅* * * */aBattercake = new EggDecorator(aBattercake);/*** 這個(gè)時(shí)候小伙伴還需要一個(gè)雞蛋* 那就加一個(gè)雞蛋* * */aBattercake = new EggDecorator(aBattercake);/*** 然后這個(gè)小伙伴還是覺得有點(diǎn)餓* 加一根香腸* * */aBattercake = new SausageDecorator(aBattercake);/*** 這個(gè)時(shí)候我們就要打印一下* 老板煎餅也做完了* 價(jià)格從這個(gè)程序里跑了一下* 我們看一下結(jié)果* 煎餅* 加一個(gè)雞蛋* 又加一個(gè)雞蛋* 加一根香腸* 銷售價(jià)格8元加1元加1元加兩元* 12元* 那這樣通過裝飾者* 就把這個(gè)煎餅裝飾出來了* 那現(xiàn)在講到這里* 我們看一下前面悄悄地埋了一個(gè)伏筆* 我們看一下抽象的裝飾者類* * */System.out.println(aBattercake.getDesc()+" 銷售價(jià)格:"+aBattercake.cost());}
}
它是一個(gè)AbstractDecorator,那裝飾者模式在實(shí)際的應(yīng)用場(chǎng)景,也比較多,當(dāng)然怎么用還是要看應(yīng)用模型的,如果這個(gè)業(yè)務(wù)模型抽象可以的話,打個(gè)比方,假設(shè)有一個(gè)訂單,而這個(gè)訂單是一個(gè)旅行訂單,旅行保險(xiǎn),我們想象一下,是否可以用保險(xiǎn)裝飾一下,和雞蛋裝飾煎餅是一樣的,當(dāng)然這個(gè)是需要看實(shí)際的業(yè)務(wù)模型,而且還有難度,那這里只是打個(gè)比方,那這個(gè)裝飾者模式就講到這里面
?
總結(jié)
- 上一篇: 外观模式源码解析(springjdbc+
- 下一篇: 装饰者模式源码解析(spring-ses