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

作者已經(jīng)搬遷去隔壁網(wǎng)站,也歡迎大家關(guān)注我們的寫作團(tuán)隊(duì):天星技術(shù)團(tuán)隊(duì)

閑聊

最近一直不在狀態(tài),月初就被博客質(zhì)量的事給弄的情緒低落,之后群里又走了兩個(gè)朋友,心情是一直在低谷徘徊,博客也是不想寫,狀態(tài)一天不如一天,總之就是一句話,不想工作。所以……
有沒有小(fu)姐(luo)姐(li)私聊我啊!

設(shè)計(jì)模式剛?cè)腴T的小伙伴可以先看看這篇《設(shè)計(jì)模式入門》,在文章末尾也將列出“設(shè)計(jì)模式系列”文章。歡迎大家關(guān)注留言投幣丟香蕉。

什么是裝飾者模式

為了方便理解,我們先舉一些例子。
人是一個(gè)類,要上街的話,人就得穿衣服,褲子,鞋子,這是最簡(jiǎn)單的裝扮,有些人還會(huì)打領(lǐng)帶、背包、戴帽子等等。在這個(gè)例子中,“人”是一個(gè)被裝飾者,衣服褲子鞋子是裝飾者。在整個(gè)打扮過程中,被裝飾者類是不會(huì)被更改的,產(chǎn)生變化的是裝飾者類的數(shù)量。
在吃火鍋之前,我們會(huì)調(diào)料碗。那么空碗就是被裝飾者,油、香菜、蔥花就是裝飾者;
點(diǎn)菜這一步驟,也是裝飾者模式,鴛鴦鍋就是被裝飾者,麻辣牛肉、毛肚、鴨腸、菌肝、千層肚、鴨血、紅糖糍粑就是裝飾者;
裝飾模式就是在不改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能。
聊到這里,有沒有一點(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)里的文字來記憶的,會(huì)比較容易記住。剛剛我們舉例寫到的“人”就是抽象組件;“男人\女人”就是具體組件;“裝飾品”就是抽象組件;“帽子”就是具體組件;

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

  1. 裝飾者和被裝飾者有相同的超類型
  2. 可以用一個(gè)或者多個(gè)裝飾者包裝一個(gè)對(duì)象
  3. 任何需要被裝飾者對(duì)象的場(chǎng)合,可以用裝飾過的對(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í)。

活生生的例子

我們先來分析一下上面提到的人化妝的例子。首先來創(chuàng)建四個(gè)參與者。
1.抽象組件Human ,給他一個(gè)自我介紹的描述,再添加一個(gè)方法,說出自己穿了什么。

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寫好了。那接下來就是包裝了。
穿衣服時(shí),我們會(huì)一件一件的穿(沒有誰(shuí)會(huì)同時(shí)穿吧?),所以,我們穿了褲子后,再穿衣服時(shí),被裝飾者是“男\(zhòng)女人+褲子”??磮D!


裝飾者是一個(gè)一個(gè)去包裹被裝飾者,這里要注意,衣服和褲子跟男人是同一個(gè)超類,我們?cè)诎臅r(shí)候,需要把被包裝的對(duì)象(被包裝的對(duì)象可能是具體組件,也可能是已經(jīng)被裝飾者裝飾之后的具體組件),傳到裝飾者中,所以我們需要在具體裝飾者類中添加一個(gè)超類參數(shù)。這樣才能得到被包裝對(duì)象的所有參數(shù)。剛剛的具體裝飾者類還沒寫完,補(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é)一下

通過例子應(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ì)模式的初衷,是為了看源碼?,F(xiàn)在我們就來一起來看看源碼, 我也會(huì)順便把我在這當(dāng)中學(xué)到的一些看源碼的方式方法說出來。大佬們請(qǐng)忽略這句話。
查看源碼技巧一:查看它的父類子類并畫圖。
在AS右上角有個(gè)hierarchy按鈕,點(diǎn)它可以查看當(dāng)前類的直接父類和全部子類。沒有這個(gè)按鈕的話就按F4。

java I/O 是比較龐大的一個(gè)庫(kù),如果直接看其代碼,很難知道每個(gè)類都在干啥,也不知道它究竟怎么運(yùn)作的。實(shí)話說,我以前就看暈了。
通過看它的類結(jié)構(gòu)。我們能畫出這樣一張圖:



在這個(gè)設(shè)計(jì)中,InputStream就是抽象組件,F(xiàn)ilterInputStream就是抽象裝飾者, StringBufferInputStream、ByteArrayInputStream等是具體組件,LineNumberInputStream、DataInputStream、BufferedInputStream等是具體裝飾者。
幾個(gè)具體組件提供了不同類型的基本字節(jié)讀取功能。
具體裝飾類提供了額外的功能。例如:BufferedInputStream提供readline()方法。

源碼擴(kuò)展

接下來我們?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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容