閒聊
最近一直不在狀態,月初就被部落格質量的事給弄的情緒低落,之後群裡又走了兩個朋友,心情是一直在低谷徘徊,部落格也是不想寫,狀態一天不如一天,總之就是一句話,不想工作。所以…… 有沒有小(fu)姐(luo)姐(li)私聊我啊!
設計模式剛入門的小夥伴可以先看看這篇《設計模式入門》,在文章末尾也將列出“設計模式系列”文章。歡迎大家關注留言投幣丟香蕉。天星技術團QQ:557247785。
什麼是裝飾者模式
為了方便理解,我們先舉一些例子。
人是一個類,要上街的話,人就得穿衣服,褲子,鞋子,這是最簡單的裝扮,有些人還會打領帶、揹包、戴帽子等等。在這個例子中,“人”是一個被裝飾者,衣服褲子鞋子是裝飾者。在整個打扮過程中,被裝飾者類是不會被更改的,產生變化的是裝飾者類的數量。
在吃火鍋之前,我們會調料碗。那麼空碗就是被裝飾者,油、香菜、蔥花就是裝飾者;
點菜這一步驟,也是裝飾者模式,鴛鴦鍋就是被裝飾者,麻辣牛肉、毛肚、鴨腸、菌肝、千層肚、鴨血、紅糖餈粑就是裝飾者;
裝飾模式就是在不改變原類檔案和使用繼承的情況下,動態地擴充套件一個物件的功能。
聊到這裡,有沒有一點餓?
走進裝飾者模式
首先看一下裝飾者模式的UML圖
可以看出裝飾模式中,有四個參與者:- Component(抽象元件): 定義物件的介面\抽象類,以規範準備接收附加責任的物件。
- ConcreteComponent(具體元件): 具體的物件,抽象裝飾者能給他新增職責
- Decorator(抽象裝飾者): 持有一個抽象元件物件的例項,並定義一個與抽象元件一致的介面。
- ConcreteDecorator(具體裝飾者): 具體的裝飾物件。給內部持有的具體元件增加具體的職責;
我是按括號裡的文字來記憶的,會比較容易記住。剛剛我們舉例寫到的“人”就是抽象元件;“男人\女人”就是具體元件;“裝飾品”就是抽象元件;“帽子”就是具體元件;
裝飾模式的特點
- 裝飾者和被裝飾者有相同的超型別
- 可以用一個或者多個裝飾者包裝一個物件
- 任何需要被裝飾者物件的場合,可以用裝飾過的物件代替它。(其實就是因為特點一)
- 裝飾者可以在所委託被裝飾者的行為之前與/或之後,加上自己的行為。(很重要)
- 物件可以在任何時候被裝飾,包括執行時。
裝飾模式的使用場合
- 在不影響其他物件的情況下,以動態、透明的方式給單個物件增加/撤銷職責。
- 當不能採用生成子類的方法進行擴充時。
活生生的例子
我們先來分析一下上面提到的人化妝的例子。首先來建立四個參與者。 1.抽象元件Human ,給他一個自我介紹的描述,再新增一個方法,說出自己穿了什麼。
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 "我穿了內褲"
}
}
複製程式碼
- 繼承抽象元件的抽象裝飾者Decoration
abstract class Decoration : Human() {
abstract override var description: String
}
複製程式碼
- 繼承抽象裝飾者的具體裝飾者,這裡我寫一個帽子類,其他的隨意新增。
class Hats : Decoration() {
override var description: String
get() = " 我有帽子"
set(value) {}
override fun getDress(): String {
return "帽子"
}
}
複製程式碼
現在有了四個參與者,結構也按照UML寫好了。那接下來就是包裝了。 穿衣服時,我們會一件一件的穿(沒有誰會同時穿吧?),所以,我們穿了褲子後,再穿衣服時,被裝飾者是“男\女人+褲子”。看圖!
裝飾者是一個一個去包裹被裝飾者,這裡要注意,衣服和褲子跟男人是同一個超類,我們在包裹的時候,需要把被包裝的物件(被包裝的物件可能是具體元件,也可能是已經被裝飾者裝飾之後的具體元件),傳到裝飾者中,所以我們需要在具體裝飾者類中新增一個超類引數。這樣才能得到被包裝物件的所有引數。所以剛剛的具體裝飾者類還沒寫完,補充完整應該是:
class Hats(var human: Human) : Decoration() {
override var description: String
get() = "帽子"
set(value) {}
override fun getDress(): String {
return human.getDress() + " 帽子"
}
}
複製程式碼
再創造幾個具體裝飾者類,此時專案結構就是這樣的。
現在我們創造一個超人,給她穿上帽子,斗篷,並在介面中顯示一下自己穿了些什麼。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的時候也只需要記住一下幾步:
- 按照裝飾模式UML圖,寫出四個參與者類。
- 在具體裝飾者類的主建構函式中新增超類引數
- 父類引用指向子類物件創造具體元件
- 用具體裝飾者裝飾物件(需要什麼裝飾什麼)
裝飾模式優點: 裝飾模式比普通的繼承更加靈活,能夠在執行時更改元件的功能。而繼承的類在執行前就決定了所有功能。
缺點: 會創造很多的小類。別人看程式碼的時候會很腦殼痛。
注意: 裝飾模式中,裝飾的順序很重要。先穿“褲子”再穿“衣服”跟先穿“衣服”後穿“褲子”是不一樣的
原始碼中的裝飾模式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()
}
複製程式碼
第一次寫關於原始碼的東西,寫的不好的地方,多多提意見。
下一次我將寫代理模式,也會講到裝飾模式和代理模式的區別,和本章未提到的裝飾模式的透明性。
以下是我“設計模式系列”文章,歡迎大家關注留言投幣丟香蕉。