Kotlin的裝飾者模式與源碼擴展

做者 點先生 日期 2018.8.26java

閒聊

最近一直不在狀態,月初就被博客質量的事給弄的情緒低落,以後羣裏又走了兩個朋友,心情是一直在低谷徘徊,博客也是不想寫,狀態一天不如一天,總之就是一句話,不想工做。因此…… 有沒有小(fu)姐(luo)姐(li)私聊我啊!設計模式

設計模式剛入門的小夥伴能夠先看看這篇《設計模式入門》,在文章末尾也將列出「設計模式系列」文章。歡迎你們關注留言投幣丟香蕉。天星技術團QQ:557247785。bash

什麼是裝飾者模式

爲了方便理解,咱們先舉一些例子。
人是一個類,要上街的話,人就得穿衣服,褲子,鞋子,這是最簡單的裝扮,有些人還會打領帶、揹包、戴帽子等等。在這個例子中,「人」是一個被裝飾者衣服褲子鞋子是裝飾者。在整個打扮過程當中,被裝飾者類是不會被更改的,產生變化的是裝飾者類的數量。
在吃火鍋以前,咱們會調料碗。那麼空碗就是被裝飾者,油、香菜、蔥花就是裝飾者;
點菜這一步驟,也是裝飾者模式,鴛鴦鍋就是被裝飾者,麻辣牛肉、毛肚、鴨腸、菌肝、千層肚、鴨血、紅糖餈粑就是裝飾者;
裝飾模式就是在不改變原類文件和使用繼承的狀況下,動態地擴展一個對象的功能。
聊到這裏,有沒有一點餓? app

如今呢?

走進裝飾者模式

首先看一下裝飾者模式的UML圖 ide

能夠看出裝飾模式中,有四個參與者:

  1. Component(抽象組件): 定義對象的接口\抽象類,以規範準備接收附加責任的對象。
  2. ConcreteComponent(具體組件): 具體的對象,抽象裝飾者能給他新增職責
  3. Decorator(抽象裝飾者): 持有一個抽象組件對象的實例,並定義一個與抽象組件一致的接口。
  4. ConcreteDecorator(具體裝飾者): 具體的裝飾對象。給內部持有的具體組件增長具體的職責;

我是按括號裏的文字來記憶的,會比較容易記住。剛剛咱們舉例寫到的「人」就是抽象組件;「男人\女人」就是具體組件;「裝飾品」就是抽象組件;「帽子」就是具體組件;函數

裝飾模式的特色

  1. 裝飾者和被裝飾者有相同的超類型
  2. 能夠用一個或者多個裝飾者包裝一個對象
  3. 任何須要被裝飾者對象的場合,能夠用裝飾過的對象代替它。(其實就是由於特色一)
  4. 裝飾者能夠在所委託被裝飾者的行爲以前與/或以後,加上本身的行爲。(很重要)
  5. 對象能夠在任什麼時候候被裝飾,包括運行時。

裝飾模式的使用場合

  1. 在不影響其餘對象的狀況下,以動態、透明的方式給單個對象增長/撤銷職責。
  2. 當不能採用生成子類的方法進行擴充時。

活生生的例子

咱們先來分析一下上面提到的人化妝的例子。首先來建立四個參與者。 1.抽象組件Human ,給他一個自我介紹的描述,再添加一個方法,說出本身穿了什麼。post

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 "我穿了內褲"
    }
}
複製代碼
  1. 繼承抽象組件的抽象裝飾者Decoration
abstract class Decoration : Human() {
    abstract override var description: String
}
複製代碼
  1. 繼承抽象裝飾者的具體裝飾者,這裏我寫一個帽子類,其餘的隨意添加。
class Hats : Decoration() {
    override var description: String
        get() = " 我有帽子"
        set(value) {}

    override fun getDress(): String {
        return "帽子"
    }
}
複製代碼

如今有了四個參與者,結構也按照UML寫好了。那接下來就是包裝了。 穿衣服時,咱們會一件一件的穿(沒有誰會同時穿吧?),因此,咱們穿了褲子後,再穿衣服時,被裝飾者是「男\女人+褲子」。看圖! 學習

裝飾者是一個一個去包裹被裝飾者,這裏要注意,衣服和褲子跟男人是同一個超類,咱們在包裹的時候,須要把被包裝的對象(被包裝的對象多是具體組件,也多是已經被裝飾者裝飾以後的具體組件),傳到裝飾者中,因此咱們須要在具體裝飾者類中添加一個超類參數。這樣才能獲得被包裝對象的全部參數。因此剛剛的具體裝飾者類還沒寫完,補充完整應該是:ui

class Hats(var human: Human) : Decoration() {
    override var description: String
        get() = "帽子"
        set(value) {}

    override fun getDress(): String {
        return human.getDress() + " 帽子"
    }
}
複製代碼

再創造幾個具體裝飾者類,此時項目結構就是這樣的。 this

如今咱們創造一個超人,給她穿上帽子,斗篷,並在界面中顯示一下本身穿了些什麼。

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()
    }
}
複製代碼

結果以下:

總結一下

經過例子應該對裝飾模式有個初步的理解了。再寫demo的時候也只須要記住一下幾步:

  1. 按照裝飾模式UML圖,寫出四個參與者類。
  2. 在具體裝飾者類的主構造函數中添加超類參數
  3. 父類引用指向子類對象創造具體組件
  4. 用具體裝飾者裝飾對象(須要什麼裝飾什麼)

裝飾模式優勢: 裝飾模式比普通的繼承更加靈活,可以在運行時更改組件的功能。而繼承的類在運行前就決定了全部功能。
缺點: 會創造不少的小類。別人看代碼的時候會很腦袋痛。
注意: 裝飾模式中,裝飾的順序很重要。先穿「褲子」再穿「衣服」跟先穿「衣服」後穿「褲子」是不同的

源碼中的裝飾模式Java I/O

我當初決定要學習設計模式的初衷,是爲了看源碼。如今咱們就來一塊兒來看看源碼, 我也會順便把我在這當中學到的一些看源碼的方式方法說出來。大佬們請忽略這句話。
查看源碼技巧一:查看它的父類子類並畫圖。
在AS右上角有個hierarchy按鈕,點它能夠查看當前類的直接父類和所有子類。

java I/O 是比較龐大的一個庫,若是直接看其代碼,很難知道每一個類都在幹啥,也不知道它究竟怎麼運做的。實話說,我之前就看暈了。 經過看它的類結構。咱們能畫出這樣一張圖:

在這個設計中,InputStream就是抽象組件,FilterInputStream就是抽象裝飾者, StringBufferInputStream、ByteArrayInputStream等是具體組件,LineNumberInputStream、DataInputStream、BufferedInputStream等是具體裝飾者。
幾個具體組件提供了不一樣類型的基本字節讀取功能。 具體裝飾類提供了額外的功能。例如:BufferedInputStream提供readline()方法。

源碼擴展

接下來咱們試試自編寫一個新的具體裝飾者類。

//將全部大寫字符轉爲小寫
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
    }
}
複製代碼

此處須要實現兩個方法,一個針對字節,一個針對字節組。

try {
            var inputStream : InputStream =
                    LowerCaseInputSteam(BufferedInputStream(FileInputStream("手機文件路徑")))
            c = inputStream.read()
            while (c!! >0) {
                stringBuffer?.append(c!!)
                c = inputStream.read()
            }
            tv_textview.text = stringBuffer.toString()
        }catch (e : IOException){
            e.printStackTrace()
        }
複製代碼

第一次寫關於源碼的東西,寫的很差的地方,多多提意見。
下一次我將寫代理模式,也會講到裝飾模式和代理模式的區別,和本章未提到的裝飾模式的透明性。

如下是我「設計模式系列」文章,歡迎你們關注留言投幣丟香蕉。

設計模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與源碼擴展
由淺到深瞭解工廠模式

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息