裝飾者模式
UML類圖
模式說明
裝飾者模式,在不改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)擴(kuò)展一個(gè)對(duì)象的功能。它是通過創(chuàng)建一個(gè)包裝對(duì)象,也就是裝飾來(lái)包裹真實(shí)的對(duì)象。
裝飾者模式具備以下特點(diǎn):
- 裝飾對(duì)象和真實(shí)對(duì)象具有相同的接口。這樣客戶端對(duì)象就能以和真實(shí)對(duì)象相同的方式和裝飾對(duì)象交互
- 裝飾對(duì)象包含一個(gè)真實(shí)對(duì)象的引用(reference)
- 裝飾對(duì)象接受所有來(lái)自客戶端的請(qǐng)求。它把這些請(qǐng)求轉(zhuǎn)發(fā)給真實(shí)的對(duì)象
設(shè)計(jì)原則
- 多用組合,少用繼承
- 類應(yīng)該設(shè)計(jì)的對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉
Java IO流是最有名的裝飾者模式案例
假裝還原一下InputStream設(shè)計(jì)過程
基本功能實(shí)現(xiàn):FileInputStream
如何實(shí)現(xiàn)read()方法?
如何實(shí)現(xiàn)read(byte b[], int off, int len)方法?
基本功能就算是實(shí)現(xiàn)了
擴(kuò)展需求
找個(gè)最簡(jiǎn)單的
需求
復(fù)制一個(gè)100M以上的文件,為了減少內(nèi)存占用節(jié)約內(nèi)存控空間,只能用很小的緩存來(lái)中轉(zhuǎn)數(shù)據(jù)
喪心病狂復(fù)制粘貼了100多M的hello.txt
緩存空間用了100字節(jié),用FileInputStream和FileOutputStream:
不是很理想啊,加大緩存空間,用1024大小的緩存作為中轉(zhuǎn)呢
用1024*4大小的緩存作為中轉(zhuǎn)呢
封裝
緩存思想:字節(jié)流一次讀一個(gè)數(shù)組的速度比一次讀一個(gè)字節(jié)要快,且讀一個(gè)大字節(jié)數(shù)組比分成多個(gè)小字節(jié)數(shù)組要快
緩存功能很好,在每次使用比較小段的數(shù)據(jù)的時(shí)候,可以先從緩存里面讀,緩存讀完了再去讀文件來(lái)填充緩存,這樣能節(jié)省內(nèi)存的同時(shí)提升性能
沒準(zhǔn)以后還會(huì)用到這樣的功能,怎么集成這個(gè)功能呢?假如在這個(gè)功能的基礎(chǔ)上,還想添加更多的功能呢?
- 讀出來(lái)就是基本類型數(shù)據(jù),而不是字節(jié)或字節(jié)數(shù)組
假裝我來(lái)考慮如何擴(kuò)展輸入流
- 在FileInputStream類里面加代碼?
違反了開閉原則:對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉
- 繼承FileInputStream寫子類?
以后還需要其他功能時(shí),繼續(xù)繼承,就會(huì)出現(xiàn)很多子類。
違反了依賴倒置原則:要依賴于抽象,不要依賴于具體
最終方案:
合成復(fù)用原則:如果新對(duì)象的某些功能在別的已經(jīng)創(chuàng)建好的對(duì)象里面已經(jīng)實(shí)現(xiàn),那么盡量使用別的對(duì)象提供的功能,使之成為新對(duì)象的一部分,而不要自己再重新創(chuàng)建。新對(duì)象通過向這些對(duì)象的委派達(dá)到復(fù)用已有功能的
- 新建一個(gè)類,來(lái)封裝這個(gè)緩存的功能
- 這個(gè)類持有一個(gè)InputStream的實(shí)現(xiàn)對(duì)象,用以利用已經(jīng)被FileInputStream實(shí)現(xiàn)的基本功能
- 新類的方法最好也和InputStream里面的方法同名,便于使用。那就直接繼承InputStream抽象類唄
誒?這兩個(gè)很像嘛
剛剛用FileInputStream寫的代碼,換成BufferedInputStream來(lái)寫:
Java InputStream裝飾類FilterInputStream的子類:
多個(gè)裝飾類的嵌套使用:
《大話設(shè)計(jì)模式》中的例子
- 穿西裝(穿皮鞋(打領(lǐng)帶(的小菜)))
- 穿西裝(打領(lǐng)帶(穿皮鞋(的小菜)))
適用范圍
系統(tǒng)需要新功能時(shí),且這些新東西只為了滿足一些在某種特定情況下才會(huì)執(zhí)行的特殊行為的需要
使用技巧
- 只有一個(gè)ConcreteComponent,Decorator可以是其子類
- 只有一個(gè)ConcreteDecorator,就不需要Decorator
注意事項(xiàng)
多個(gè)裝飾者之間的裝飾順序需要按照實(shí)際的邏輯進(jìn)行。
多個(gè)裝飾者盡量互相獨(dú)立,可以隨意順序嵌套
裝飾者模式和代理模式的區(qū)別
裝飾者模式:以對(duì)客戶端透明的方式擴(kuò)展對(duì)象的功能,是繼承關(guān)系的一個(gè)替代方案,為所裝飾的對(duì)象增強(qiáng)功能;
代理模式:給一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并有代理對(duì)象來(lái)控制對(duì)原有對(duì)象的引用,對(duì)代理的對(duì)象施加控制,并不提供對(duì)象本身的增強(qiáng)功能。
模板方法模式
UML類圖
模式說明
抽象類(AbstractClass):
——定義抽象的 原語(yǔ)操作(primitive operation) ,具體的子類將重定義它們以實(shí)現(xiàn)一個(gè)算法的各個(gè)步驟。
——實(shí)現(xiàn)一個(gè)模板方法,定義一個(gè)算法的骨架。該模板方法不僅調(diào)用原語(yǔ)操作,也調(diào)用定義在 AbstractClass 或其他對(duì)象中的操作
具體子類 (ConcreteClass):
——實(shí)現(xiàn)原語(yǔ)操作以完成算法中與特定子類相關(guān)的步驟。
意圖
定義一個(gè)操作中的算法骨架,而將一些步驟延遲到子類中, 使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重新定義該算法的某些特定步驟。
很久以前,onCreate()需要做很多工作
onCreate()里面就有很多代碼,以后看代碼非常吃力。設(shè)置一個(gè)父類來(lái)規(guī)范代碼:
實(shí)際的Activity再繼承BaseActivity:
沉浸式狀態(tài)欄
項(xiàng)目中的Component、ComponentContainer
適用性
模板方法應(yīng)用于下列情況:
- 一次性實(shí)現(xiàn)一個(gè)算法的不變的部分,并將可變的行為留給子類來(lái)實(shí)現(xiàn)。
- 各子類中公共的行為應(yīng)被提取出來(lái)并集中到一個(gè)公共父類中以避免代碼重復(fù)。首先識(shí)別現(xiàn)有代碼中的不同之處,并且將不同之處分離為新的操作。最后,用一個(gè)調(diào)用這些新的操作的模板方法來(lái)替換這些不同的代碼。
- 控制子類擴(kuò)展。模板方法只在特定點(diǎn)調(diào)用“ hook”操作 ,這樣就只允許在這些點(diǎn)進(jìn)行擴(kuò)展。