Kotlin的裝飾者模式與源碼擴(kuò)展

作者已經(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è)參與者:

  1. Component(抽象組件):定義對(duì)象的接口\抽象類,以規(guī)范準(zhǔn)備接收附加責(zé)任的對(duì)象详幽。
  2. ConcreteComponent(具體組件):具體的對(duì)象筛欢,抽象裝飾者能給他新增職責(zé)
  3. Decorator(抽象裝飾者):持有一個(gè)抽象組件對(duì)象的實(shí)例浸锨,并定義一個(gè)與抽象組件一致的接口。
  4. ConcreteDecorator(具體裝飾者):具體的裝飾對(duì)象版姑。給內(nèi)部持有的具體組件增加具體的職責(zé)柱搜;

我是按括號(hào)里的文字來(lái)記憶的,會(huì)比較容易記住剥险。剛剛我們舉例寫到的“人”就是抽象組件冯凹;“男人\女人”就是具體組件;“裝飾品”就是抽象組件炒嘲;“帽子”就是具體組件;

裝飾模式的特點(diǎn)

  1. 裝飾者和被裝飾者有相同的超類型
  2. 可以用一個(gè)或者多個(gè)裝飾者包裝一個(gè)對(duì)象
  3. 任何需要被裝飾者對(duì)象的場(chǎng)合匈庭,可以用裝飾過(guò)的對(duì)象代替它夫凸。(其實(shí)就是因?yàn)樘攸c(diǎn)一)
  4. 裝飾者可以在所委托被裝飾者的行為之前與/或之后,加上自己的行為阱持。(很重要)
  5. 對(duì)象可以在任何時(shí)候被裝飾夭拌,包括運(yùn)行時(shí)。

裝飾模式的使用場(chǎng)合

  1. 在不影響其他對(duì)象的情況下衷咽,以動(dòng)態(tài)鸽扁、透明的方式給單個(gè)對(duì)象增加/撤銷職責(zé)。
  2. 當(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
}
  1. 繼承抽象組件的具體組件Male\Female慰于,改變下自我描述。
class Male : Human() {
    override var description: String
        get() = "我是男性"
        set(value) {}

    override fun getDress(): String {
        return "我穿了內(nèi)褲"
    }
}
  1. 繼承抽象組件的抽象裝飾者Decoration
abstract class Decoration : Human() {
    abstract override var description: String
}
  1. 繼承抽象裝飾者的具體裝飾者唤衫,這里我寫一個(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í)候也只需要記住一下幾步:

  1. 按照裝飾模式UML圖身冬,寫出四個(gè)參與者類。
  2. 在具體裝飾者類的主構(gòu)造函數(shù)中添加超類參數(shù)
  3. 父類引用指向子類對(duì)象創(chuàng)造具體組件
  4. 用具體裝飾者裝飾對(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,年輕小伙竟作出這種事券膀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末君纫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子芹彬,更是在濱河造成了極大的恐慌蓄髓,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舒帮,死亡現(xiàn)場(chǎng)離奇詭異会喝,居然都是意外死亡陡叠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門好乐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)匾竿,“玉大人,你說(shuō)我怎么就攤上這事蔚万×胙” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵反璃,是天一觀的道長(zhǎng)昵慌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)淮蜈,這世上最難降的妖魔是什么斋攀? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮梧田,結(jié)果婚禮上淳蔼,老公的妹妹穿的比我還像新娘。我一直安慰自己裁眯,他們只是感情好鹉梨,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著穿稳,像睡著了一般存皂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逢艘,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天旦袋,我揣著相機(jī)與錄音,去河邊找鬼它改。 笑死疤孕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的央拖。 我是一名探鬼主播胰柑,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼爬泥!你這毒婦竟也來(lái)了柬讨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤袍啡,失蹤者是張志新(化名)和其女友劉穎踩官,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體境输,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔗牡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年颖系,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辩越。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嘁扼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出黔攒,到底是詐尸還是另有隱情趁啸,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布督惰,位于F島的核電站不傅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏赏胚。R本人自食惡果不足惜访娶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望觉阅。 院中可真熱鬧崖疤,春花似錦、人聲如沸典勇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)痴柔。三九已至,卻和暖如春疫向,著一層夾襖步出監(jiān)牢的瞬間咳蔚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工搔驼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谈火,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓舌涨,卻偏偏與公主長(zhǎng)得像糯耍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子囊嘉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容