作者已經(jīng)搬遷去隔壁網(wǎng)站唱凯,也歡迎大家關(guān)注我們的寫作團(tuán)隊(duì):天星技術(shù)團(tuán)隊(duì)尔许。
閑聊
最近一直不在狀態(tài),月初就被博客質(zhì)量的事給弄的情緒低落诀浪,之后群里又走了兩個(gè)朋友棋返,心情是一直在低谷徘徊,博客也是不想寫笋妥,狀態(tài)一天不如一天懊昨,總之就是一句話,不想工作春宣。所以……
有沒(méi)有小(fu)姐(luo)姐(li)私聊我敖桶洹!
設(shè)計(jì)模式剛?cè)腴T的小伙伴可以先看看這篇《設(shè)計(jì)模式入門》月帝,在文章末尾也將列出“設(shè)計(jì)模式系列”文章躏惋。歡迎大家關(guān)注留言投幣丟香蕉。
什么是裝飾者模式
為了方便理解嚷辅,我們先舉一些例子簿姨。
人是一個(gè)類,要上街的話,人就得穿衣服扁位,褲子准潭,鞋子,這是最簡(jiǎn)單的裝扮域仇,有些人還會(huì)打領(lǐng)帶刑然、背包、戴帽子等等暇务。在這個(gè)例子中泼掠,“人”是一個(gè)被裝飾者,衣服褲子鞋子是裝飾者垦细。在整個(gè)打扮過(guò)程中择镇,被裝飾者類是不會(huì)被更改的,產(chǎn)生變化的是裝飾者類的數(shù)量括改。
在吃火鍋之前腻豌,我們會(huì)調(diào)料碗。那么空碗就是被裝飾者叹谁,油饲梭、香菜、蔥花就是裝飾者焰檩;
點(diǎn)菜這一步驟憔涉,也是裝飾者模式,鴛鴦鍋就是被裝飾者析苫,麻辣牛肉兜叨、毛肚、鴨腸衩侥、菌肝国旷、千層肚、鴨血茫死、紅糖糍粑就是裝飾者跪但;
裝飾模式就是在不改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能峦萎。
聊到這里屡久,有沒(méi)有一點(diǎn)餓?
現(xiàn)在呢爱榔?
走進(jìn)裝飾者模式
首先看一下裝飾者模式的UML圖
可以看出裝飾模式中被环,有四個(gè)參與者:
- Component(抽象組件):定義對(duì)象的接口\抽象類,以規(guī)范準(zhǔn)備接收附加責(zé)任的對(duì)象详幽。
- ConcreteComponent(具體組件):具體的對(duì)象筛欢,抽象裝飾者能給他新增職責(zé)
- Decorator(抽象裝飾者):持有一個(gè)抽象組件對(duì)象的實(shí)例浸锨,并定義一個(gè)與抽象組件一致的接口。
- ConcreteDecorator(具體裝飾者):具體的裝飾對(duì)象版姑。給內(nèi)部持有的具體組件增加具體的職責(zé)柱搜;
我是按括號(hào)里的文字來(lái)記憶的,會(huì)比較容易記住剥险。剛剛我們舉例寫到的“人”就是抽象組件冯凹;“男人\女人”就是具體組件;“裝飾品”就是抽象組件炒嘲;“帽子”就是具體組件;
裝飾模式的特點(diǎn)
- 裝飾者和被裝飾者有相同的超類型
- 可以用一個(gè)或者多個(gè)裝飾者包裝一個(gè)對(duì)象
- 任何需要被裝飾者對(duì)象的場(chǎng)合匈庭,可以用裝飾過(guò)的對(duì)象代替它夫凸。(其實(shí)就是因?yàn)樘攸c(diǎn)一)
- 裝飾者可以在所委托被裝飾者的行為之前與/或之后,加上自己的行為阱持。(很重要)
- 對(duì)象可以在任何時(shí)候被裝飾夭拌,包括運(yùn)行時(shí)。
裝飾模式的使用場(chǎng)合
- 在不影響其他對(duì)象的情況下衷咽,以動(dòng)態(tài)鸽扁、透明的方式給單個(gè)對(duì)象增加/撤銷職責(zé)。
- 當(dāng)不能采用生成子類的方法進(jìn)行擴(kuò)充時(shí)镶骗。
活生生的例子
我們先來(lái)分析一下上面提到的人化妝的例子桶现。首先來(lái)創(chuàng)建四個(gè)參與者。
1.抽象組件Human 鼎姊,給他一個(gè)自我介紹的描述骡和,再添加一個(gè)方法,說(shuō)出自己穿了什么相寇。
abstract class Human {
open var description = "I'm a human."
abstract fun getDress() : String
}
- 繼承抽象組件的具體組件Male\Female慰于,改變下自我描述。
class Male : Human() {
override var description: String
get() = "我是男性"
set(value) {}
override fun getDress(): String {
return "我穿了內(nèi)褲"
}
}
- 繼承抽象組件的抽象裝飾者Decoration
abstract class Decoration : Human() {
abstract override var description: String
}
- 繼承抽象裝飾者的具體裝飾者唤衫,這里我寫一個(gè)帽子類婆赠,其他的隨意添加。
class Hats : Decoration() {
override var description: String
get() = " 我有帽子"
set(value) {}
override fun getDress(): String {
return "帽子"
}
}
現(xiàn)在有了四個(gè)參與者佳励,結(jié)構(gòu)也按照UML寫好了休里。那接下來(lái)就是包裝了。
穿衣服時(shí)植兰,我們會(huì)一件一件的穿(沒(méi)有誰(shuí)會(huì)同時(shí)穿吧份帐?),所以楣导,我們穿了褲子后废境,再穿衣服時(shí),被裝飾者是“男\(zhòng)女人+褲子”∝迹看圖巴元!
裝飾者是一個(gè)一個(gè)去包裹被裝飾者,這里要注意驮宴,衣服和褲子跟男人是同一個(gè)超類逮刨,我們?cè)诎臅r(shí)候,需要把被包裝的對(duì)象(被包裝的對(duì)象可能是具體組件堵泽,也可能是已經(jīng)被裝飾者裝飾之后的具體組件)修己,傳到裝飾者中,所以我們需要在具體裝飾者類中添加一個(gè)超類參數(shù)迎罗。這樣才能得到被包裝對(duì)象的所有參數(shù)睬愤。剛剛的具體裝飾者類還沒(méi)寫完,補(bǔ)充完整應(yīng)該是:
class Hats(var human: Human) : Decoration() {
override var description: String
get() = "帽子"
set(value) {}
override fun getDress(): String {
return human.getDress() + " 帽子"
}
}
再創(chuàng)造幾個(gè)具體裝飾者類纹安,此時(shí)項(xiàng)目結(jié)構(gòu)就是這樣的尤辱。
現(xiàn)在我們創(chuàng)造一個(gè)超人,給她穿上帽子厢岂,斗篷嵌莉,并在界面中顯示一下自己穿了些什么茧妒。
class MainActivity : AppCompatActivity() {
var superMan : Human? = Female()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
superMan = Cloak(Hats(this.superMan!!))
tv_textview.text = superMan?.description + superMan?.getDress()
}
}
結(jié)果如下:
總結(jié)一下
通過(guò)例子應(yīng)該對(duì)裝飾模式有個(gè)初步的理解了腐巢。再寫demo的時(shí)候也只需要記住一下幾步:
- 按照裝飾模式UML圖身冬,寫出四個(gè)參與者類。
- 在具體裝飾者類的主構(gòu)造函數(shù)中添加超類參數(shù)
- 父類引用指向子類對(duì)象創(chuàng)造具體組件
- 用具體裝飾者裝飾對(duì)象(需要什么裝飾什么)
裝飾模式優(yōu)點(diǎn):裝飾模式比普通的繼承更加靈活窗怒,能夠在運(yùn)行時(shí)更改組件的功能映跟。而繼承的類在運(yùn)行前就決定了所有功能。
缺點(diǎn):會(huì)創(chuàng)造很多的小類扬虚。別人看代碼的時(shí)候會(huì)很腦殼痛努隙。
注意:裝飾模式中,裝飾的順序很重要辜昵。先穿“褲子”再穿“衣服”跟先穿“衣服”后穿“褲子”是不一樣的
源碼中的裝飾模式Java I/O
我當(dāng)初決定要學(xué)習(xí)設(shè)計(jì)模式的初衷荸镊,是為了看源碼。現(xiàn)在我們就來(lái)一起來(lái)看看源碼堪置, 我也會(huì)順便把我在這當(dāng)中學(xué)到的一些看源碼的方式方法說(shuō)出來(lái)躬存。大佬們請(qǐng)忽略這句話。
查看源碼技巧一:查看它的父類子類并畫圖舀锨。
在AS右上角有個(gè)hierarchy按鈕岭洲,點(diǎn)它可以查看當(dāng)前類的直接父類和全部子類。沒(méi)有這個(gè)按鈕的話就按F4坎匿。
java I/O 是比較龐大的一個(gè)庫(kù)盾剩,如果直接看其代碼雷激,很難知道每個(gè)類都在干啥,也不知道它究竟怎么運(yùn)作的告私。實(shí)話說(shuō)屎暇,我以前就看暈了。
通過(guò)看它的類結(jié)構(gòu)驻粟。我們能畫出這樣一張圖:
在這個(gè)設(shè)計(jì)中根悼,InputStream就是抽象組件,F(xiàn)ilterInputStream就是抽象裝飾者蜀撑, StringBufferInputStream挤巡、ByteArrayInputStream等是具體組件,LineNumberInputStream酷麦、DataInputStream玄柏、BufferedInputStream等是具體裝飾者。
幾個(gè)具體組件提供了不同類型的基本字節(jié)讀取功能贴铜。
具體裝飾類提供了額外的功能。例如:BufferedInputStream提供readline()方法瀑晒。
源碼擴(kuò)展
接下來(lái)我們?cè)囋囎跃帉懸粋€(gè)新的具體裝飾者類绍坝。
//將所有大寫字符轉(zhuǎn)為小寫
class LowerCaseInputSteam(inputStream: InputStream) : FilterInputStream(inputStream){
override fun read(): Int {
val result = super.read()
if(result==-1) return result
else return Character.toLowerCase(result)
}
override fun read(b: ByteArray, off: Int, len: Int): Int {
val result = super.read(b, off, len)
for (i in off until off+result){
b[i] = Character.toLowerCase(b[i].toInt()).toByte()
}
return result
}
}
此處需要實(shí)現(xiàn)兩個(gè)方法,一個(gè)針對(duì)字節(jié)苔悦,一個(gè)針對(duì)字節(jié)組轩褐。
try {
var inputStream : InputStream =
LowerCaseInputSteam(BufferedInputStream(FileInputStream("手機(jī)文件路徑")))
c = inputStream.read()
while (c!! >0) {
stringBuffer?.append(c!!)
c = inputStream.read()
}
tv_textview.text = stringBuffer.toString()
}catch (e : IOException){
e.printStackTrace()
}
第一次寫關(guān)于源碼的東西,寫的不好的地方玖详,多多提意見把介。
下一次我將寫代理模式,也會(huì)講到裝飾模式和代理模式的區(qū)別蟋座,和本章未提到的裝飾模式的透明性拗踢。
以下是我“設(shè)計(jì)模式系列”文章,歡迎大家關(guān)注留言投幣丟香蕉向臀。
也可以進(jìn)群跟大神們討論巢墅。qq群:557247785
設(shè)計(jì)模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與源碼擴(kuò)展
由淺到深了解工廠模式
為了學(xué)習(xí)Rxjava,年輕小伙竟作出這種事券膀!