雖然設計模式偏重于思想層面,但是不同的編程語言有著其獨特的語法展現滴劲,這使得在某個特定語言內攻晒,可能會更靈活和更有張力的實現某些設計模式。同時班挖,對于kotlin來說鲁捏,由于其完全兼容Java,若是只是談設計模式的實現的話萧芙,完全可以把java實現的設計模式convert成kotlin就可以了给梅,但是這樣的話假丧,便會埋沒一些kotlin的特色。
Kotlin對比java而言动羽,其大大擴大了函數的靈活性:高階函數(可以接受函數作為參數)包帚,擴展函數(在一個類的外面,為其聲明新的方法运吓,靜態(tài)編譯渴邦,實例調用)以及獨立函數(不依賴于類/對象,可以獨立存在于文件中)等等拘哨,這使得其實現設計模式有其獨特的風格和張力谋梭,因此,本文的目的倦青,不僅僅是簡單的用kotlin實現設計模式瓮床,而是專注于發(fā)現kotlin語言的特色風格和張力,同時研究kotlin這個現代語言的簡潔性以及實現某些經典模式的便捷性姨夹。
本文只專注怎么用kotlin來進行比較特色的gof設計模式實現纤垂,基本不會探討這些模式的思想和優(yōu)缺點矾策,需要的自行百度其它資料磷账。本文還提供了安卓示例工程。
接下來贾虽,我們先看看一些模式的實現逃糟。
策略模式
策略模式定義了一系列的算法,并將每一個算法封裝起來蓬豁,而且使它們還可以相互替換绰咽。策略模式讓算法獨立于使用它的客戶而獨立變化。
- 定義算法抽象地粪,這里取募,不再是接口的方式,而是直接使用函數類型作為抽象蟆技;
typealias PlayVideo = () -> String
- 算法實例對象玩敏,不再是類實例的方式,而是用子類型函數的方式质礼,提供兩個旺聚;
val tv: PlayVideo = {
"用電視看視頻"http://作為返回值
}
val phone: PlayVideo = {
"---用手機看視頻---"
}
- 算法的使用場景以及調用,tv和phone對象可以相互替換眶蕉,產生不同行為
class Device(var name: String) {
fun play(p: PlayVideo): String {
return p()
}
}
result.text = device.play(tv)//電視播放視頻
result.text = device.play(phone)//手機播放視頻
整體實現思路同java類似砰粹,只是這里將函數列為了一等公民,免去了對象的創(chuàng)建和調用造挽,節(jié)省代碼碱璃,更易理解弄痹。
命令模式
命令模式:將請求封裝成對象,以便使用不同的請求嵌器、日志界酒、隊列等來參數化其他對象。命令模式也支持撤銷操作嘴秸。
- 抽象命令毁欣,聲明執(zhí)行的方法;
typealias command = (worker: Worker) -> Unit
- 命令接口實現''對象''岳掐,是“虛”的實現凭疮;通常會持有接收者,并調用接收者的功能來完成命令要執(zhí)行的操作串述,在這里执解,是以函數對象的方式出現。
var strCommand: command = { it.addStr('a') }//后綴添加字符a
var numCommand: command = { it.addNum(9) }//后綴添加字符9
- 接收者纲酗,真正執(zhí)行命令的對象衰腌。任何類都可能成為一個接收者,只要它能夠實現命令要求實現的相應功能觅赊。
class Worker {
var str: String = ""
fun addStr(s: Char) {
str += s
}
fun addNum(a: Int) {
str += a
}
fun back() {
str = str.substring(0, str.length - 1)
}
}
- 調用者右蕊,要求命令對象執(zhí)行請求,通常會持有命令對象吮螺,可以持有很多的命令對象饶囚,以供進行撤銷、日志等操作鸠补。這個是客戶端真正觸發(fā)命令并要求命令執(zhí)行相應操作的地方萝风,也就是說相當于使用命令對象的入口。
class Client(var aWorker: Worker) {
var comList = ArrayList<command>()
fun execute(com: command) {
com.invoke(aWorker)
comList.add(com)
}
fun undo() {
if (comList.size == 0) {
return
}
aWorker.back()
comList.remove(comList[comList.size - 1])
}
fun show(): String {
return aWorker.str
}
}
- 使用:創(chuàng)建具體的命令對象紫岩,并且設置命令對象的接收者
var client = Client(Worker())//創(chuàng)建調用者
client.execute(strCommand)//執(zhí)行添加字符a的命令
client.execute(numCommand)//執(zhí)行添加數字9的命令
client.undo()//撤銷上一步的命令
觀察者模式
觀察者模式:有時被稱作發(fā)布/訂閱模式规惰,觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監(jiān)聽某一個主題對象泉蝌。這個主題對象在狀態(tài)發(fā)生變化時歇万,會通知所有觀察者對象,使它們能夠自動更新自己梨与。
- 定義觀察者堕花,是以函數對象的方式進行;
typealias listener=(a: Int) -> Unit
- 被觀察者粥鞋,可以增加/刪除觀察者對象缘挽。
class Obsevable() {
var lists: ArrayList<listener> = ArrayList()
fun reg(p: listener) {
lists.add(p)
}
fun unReg(p:listener) {
lists.remove(p)
}
fun no(str: Int) {
for (x in lists) {
x.invoke(str)
}
}
}
- 調用執(zhí)行。
var observer = Obsevable()//聲明被觀察者對象
observer.reg {
result.text = "${result.text}-觀察者aaa1得到事件$it"
}//注冊添加一個觀察者,不能被取消注冊
var v3: listener = { toast("v3得到事件$it") }//聲明一個可被刪除的觀察者
observer.reg(v3)//注冊添加一個觀察者v3
observer.notify(110)//發(fā)生事件壕曼,通知觀察者
observer.unReg(v3)//刪除一個觀察者v3
通過函數對象的整合苏研,代碼實現更加簡約。
裝飾者模式
在不必改變原類文件和使用繼承的情況下腮郊,動態(tài)地擴展一個對象的功能摹蘑。它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實的對象轧飞。
裝飾模式會導致設計中出現許多小類,如果過度使用,會使程序變得很復雜衅鹿。不過Kotlin有強大的擴展函數功能,裝飾者的實現將會比較簡約过咬。
- 定義行為抽象和最基礎的行為大渤;
interface Text {
fun draw(): String
}
class DefaultText(var text: String) : Text {
override fun draw(): String {
return text
}
}
- 使用擴展函數,聲明幾個裝飾行為掸绞。
fun Text.underline(decorated: Text.() -> String): String {
return "_" + this.decorated() + "_"
}//給已有行為添加下劃線_
fun Text.bracket(decorated: Text.() -> String): String {
return "{" + this.decorated() + "}"
}//給已有行為添加花括號{}
- 調用執(zhí)行泵三。
var text = DefaultText("裝飾者")
result.text = text.draw()//基礎行為
result.text = text.underline { text.draw() }//加下劃線
result.text = text.bracket { text.underline { text.draw() } }//加下劃線,再加括號
通過擴展函數衔掸,動態(tài)添加某些對象的行為是不是相當方便烫幕。
整體就先列出這么幾個設計模式的實現吧,眼尖的童鞋可以發(fā)現敞映,這些設計模式基本都是行為模式较曼,這與kotlin強大且靈活的函數功能是分不開的。而對于其它類型的某些設計模式驱显,kotlin比較難給出比較特色的實現诗芜,以后再討論吧。
作者劉咸尚