Kotlin 如何實(shí)現(xiàn)其他常用設(shè)計(jì)模式
前兩篇文章都詳細(xì)分析了 Kotlin 如何實(shí)現(xiàn)常用的單例、代理設(shè)計(jì)模式。并且從代碼實(shí)現(xiàn)和使用場(chǎng)景分別對(duì)比了 Java 和 Kotlin。那其實(shí)總共有 23 種設(shè)計(jì)模式,不可能每一種都能詳細(xì)介紹,那么這篇文章會(huì)繼續(xù)介紹 Kotlin 實(shí)現(xiàn)其他設(shè)計(jì)模式方法,因?yàn)槠邢蓿粫?huì)特別詳細(xì)對(duì)比 Java 中的實(shí)現(xiàn)。
1. Kotlin 實(shí)現(xiàn)觀察者模式
1.1 模式的介紹
模式定義了指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。這種模式有時(shí)又稱作發(fā)布 - 訂閱模式、模型 - 視圖模式,它是對(duì)象行為型模式。
1.2 模式的特點(diǎn)
- 降低了目標(biāo)與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系;
- 目標(biāo)與觀察者之間建立了一套觸發(fā)機(jī)制;
- 當(dāng)觀察者對(duì)象很多時(shí),通知的發(fā)布會(huì)花費(fèi)很多時(shí)間,影響程序的效率。
1.3 Kotlin 代碼實(shí)現(xiàn)
Kotlin 實(shí)現(xiàn)觀察者模式可以使用 Kotlin 中內(nèi)置的 by Delegates.observable 代理輕松實(shí)現(xiàn)。
interface TextChangedListener {
fun onTextChanged(oldText: String, newText: String)
}
class PrintingTextChangedListener : TextChangedListener {
private var text = ""
override fun onTextChanged(oldText: String, newText: String) {
text = "Text is changed: $oldText -> $newText"
}
}
class TextView {
val listeners = mutableListOf<TextChangedListener>()
var text: String by Delegates.observable("") { _, old, new ->
listeners.forEach { it.onTextChanged(old, new) }
}
}
fun main() {
val textView = TextView().apply {
listener = PrintingTextChangedListener()
}
with(textView) {
text = "old text"
text = "new text"
}
}
2. Kotlin 實(shí)現(xiàn)策略模式
2.1 模式的介紹
該模式定義了一系列算法,并將每個(gè)算法封裝起來(lái),使它們可以相互替換,且算法的變化不會(huì)影響使用算法的客戶。策略模式屬于對(duì)象行為模式,它通過(guò)對(duì)算法進(jìn)行封裝,把使用算法的責(zé)任和算法的實(shí)現(xiàn)分割開(kāi)來(lái),并委派給不同的對(duì)象對(duì)這些算法進(jìn)行管理。
2.2 模式的特點(diǎn)
- 使用策略模式可以避免使用多重條件語(yǔ)句,如 if…else 語(yǔ)句、switch…case 語(yǔ)句;
- 提供了一系列的可供重用的策略實(shí)現(xiàn),適合使用繼承可以把公共代碼轉(zhuǎn)移到父類里面,從而避免重復(fù)的代碼;
- 提供了對(duì)開(kāi)閉原則的完美支持,可以在不修改原代碼的情況下,靈活增加新策略實(shí)現(xiàn);
- 策略模式會(huì)新增很多的策略類,增加維護(hù)難度。
2.3 Kotlin 代碼實(shí)現(xiàn)
class Printer(private val stringFormatterStrategy: (String) -> String) {
fun printString(string: String) {
println(stringFormatterStrategy(string))
}
}
val lowerCaseFormatter: (String) -> String = { it.toLowerCase() }
val upperCaseFormatter = { it: String -> it.toUpperCase() }
fun main() {
val inputString = "Something input"
val lowerCasePrinter = Printer(lowerCaseFormatter)
lowerCasePrinter.printString(inputString)
val upperCasePrinter = Printer(upperCaseFormatter)
upperCasePrinter.printString(inputString)
val prefixPrinter = Printer { "Prefix: $it" }
prefixPrinter.printString(inputString)
}
3. Kotlin 實(shí)現(xiàn)狀態(tài)模式
3.1 模式的介紹
該模式定義了對(duì)有狀態(tài)的對(duì)象,把復(fù)雜的 “判斷邏輯” 提取到不同的狀態(tài)對(duì)象中,允許狀態(tài)對(duì)象在其內(nèi)部狀態(tài)發(fā)生改變時(shí)改變其行為。
3.2 模式的特點(diǎn)
- 狀態(tài)類職責(zé)明確,有利于程序的擴(kuò)展。通過(guò)定義新的子類很容易地增加新的狀態(tài)和轉(zhuǎn)換;
- 結(jié)構(gòu)清晰,狀態(tài)模式將與特定狀態(tài)相關(guān)的行為局部化到一個(gè)狀態(tài)中,并且將不同狀態(tài)的行為分割開(kāi)來(lái),符合 “單一職責(zé)原則”;
- 不同的狀態(tài)引入獨(dú)立的對(duì)象中會(huì)使得狀態(tài)轉(zhuǎn)換變得更加明確,且減少對(duì)象間的相互依賴;
- 狀態(tài)模式的使用必然會(huì)增加系統(tǒng)的類與對(duì)象的個(gè)數(shù)。
3.3 Kotlin 代碼實(shí)現(xiàn)
sealed class AuthorizationState
object Unauthorized : AuthorizationState()
class Authorized(val userName: String) : AuthorizationState()
class AuthorizationPresenter {
private var state: AuthorizationState = Unauthorized
val isAuthorized: Boolean
get() = when (state) {
is Authorized -> true
is Unauthorized -> false
}
val userName: String
get() {
val state = this.state
return when (state) {
is Authorized -> state.userName
is Unauthorized -> "Unknown"
}
}
fun loginUser(userName: String) {
state = Authorized(userName)
}
fun logoutUser() {
state = Unauthorized
}
override fun toString() = "User '$userName' is logged in: $isAuthorized"
}
fun main() {
val authorizationPresenter = AuthorizationPresenter()
authorizationPresenter.loginUser("admin")
println(authorizationPresenter)
authorizationPresenter.logoutUser()
println(authorizationPresenter)
}
4. Kotlin 實(shí)現(xiàn) Builder 模式
4.1 模式的介紹
該模式定義了將一個(gè)復(fù)雜對(duì)象的構(gòu)造與它的表示分離,使同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。它是將一個(gè)復(fù)雜的對(duì)象分解為多個(gè)簡(jiǎn)單的對(duì)象,然后一步一步構(gòu)建而成。它將變與不變相分離,即產(chǎn)品的組成部分是不變的,但每一部分是可以靈活選擇的。
4.2 模式的特點(diǎn)
- 擴(kuò)展性好,各個(gè)具體的建造者相互獨(dú)立,有利于解耦。
- 封裝性好,構(gòu)建和表示分離。
- 隱藏產(chǎn)品內(nèi)部組成的細(xì)節(jié),可以對(duì)創(chuàng)建過(guò)程逐步細(xì)化,而不對(duì)其它模塊產(chǎn)生任何影響。
- 內(nèi)部變化復(fù)雜,如果產(chǎn)品內(nèi)部發(fā)生變化,則建造者也要同步修改,維護(hù)成本較大。
4.3 Kotlin 代碼實(shí)現(xiàn)
class Dialog {
fun showTitle() = println("showing title")
fun setTitle(text: String) = println("setting title text $text")
fun setTitleColor(color: String) = println("setting title color $color")
fun showMessage() = println("showing message")
fun setMessage(text: String) = println("setting message $text")
fun setMessageColor(color: String) = println("setting message color $color")
fun showImage(bitmapBytes: ByteArray) = println("showing image with size ${bitmapBytes.size}")
fun show() = println("showing dialog $this")
}
class DialogBuilder() {
constructor(init: DialogBuilder.() -> Unit) : this() {
init()
}
private var titleHolder: TextView? = null
private var messageHolder: TextView? = null
private var imageHolder: File? = null
fun title(init: TextView.() -> Unit) {
titleHolder = TextView().apply { init() }
}
fun message(init: TextView.() -> Unit) {
messageHolder = TextView().apply { init() }
}
fun image(init: () -> File) {
imageHolder = init()
}
fun build(): Dialog {
val dialog = Dialog()
titleHolder?.apply {
dialog.setTitle(text)
dialog.setTitleColor(color)
dialog.showTitle()
}
messageHolder?.apply {
dialog.setMessage(text)
dialog.setMessageColor(color)
dialog.showMessage()
}
imageHolder?.apply {
dialog.showImage(readBytes())
}
return dialog
}
class TextView {
var text: String = ""
var color: String = "#00000"
}
}
fun dialog(init: DialogBuilder.() -> Unit): Dialog {
return DialogBuilder(init).build()
}
fun main() {
val dialog: Dialog = dialog {
title {
text = "Dialog Title"
}
message {
text = "Dialog Message"
color = "#333333"
}
image {
File.createTempFile("image", "jpg")
}
}
dialog.show()
}
5. Kotlin 實(shí)現(xiàn)工廠方法模式
5.1 模式的介紹
該模式實(shí)際上是對(duì)簡(jiǎn)單工廠模式的進(jìn)一步抽象化,其好處是可以使系統(tǒng)在不修改原來(lái)代碼的情況下引進(jìn)新的產(chǎn)品,即滿足開(kāi)閉原則。
5.2 模式的特點(diǎn)
- 靈活性增強(qiáng),對(duì)于新產(chǎn)品的創(chuàng)建,只需多寫(xiě)一個(gè)相應(yīng)的工廠類;
- 高度解耦。
5.3 Kotlin 代碼實(shí)現(xiàn)
sealed class Country {
object USA : Country()
}
object Spain : Country()
class Greece(val someProperty: String) : Country()
data class Canada(val someProperty: String) : Country()
class Currency(
val code: String
)
object CurrencyFactory {
fun currencyForCountry(country: Country): Currency = when (country) {
is Greece -> Currency("EUR")
is Spain -> Currency("EUR")
is Country.USA -> Currency("USD")
is Canada -> Currency("CAD")
}
}
fun main() {
val greeceCurrency = CurrencyFactory.currencyForCountry(Greece("")).code
println("Greece currency: $greeceCurrency")
val usaCurrency = CurrencyFactory.currencyForCountry(Country.USA).code
println("USA currency: $usaCurrency")
assertThat(greeceCurrency).isEqualTo("EUR")
assertThat(usaCurrency).isEqualTo("USD")
}
6. Kotlin 實(shí)現(xiàn)抽象工廠模式
6.1 模式的介紹
該模式定義了一種為訪問(wèn)類提供一個(gè)創(chuàng)建一組相關(guān)或相互依賴對(duì)象的接口,且訪問(wèn)類無(wú)須指定所要產(chǎn)品的具體類就能得到同族的不同等級(jí)的產(chǎn)品的模式結(jié)構(gòu),它是工廠方法模式的升級(jí)版本,工廠方法模式只生產(chǎn)一個(gè)等級(jí)的產(chǎn)品,而抽象工廠模式可生產(chǎn)多個(gè)等級(jí)的產(chǎn)品。
6.2 模式的特點(diǎn)
- 可以在類的內(nèi)部對(duì)產(chǎn)品族中相關(guān)聯(lián)的多等級(jí)產(chǎn)品共同管理,而不必專門(mén)引入多個(gè)新的類來(lái)進(jìn)行管理;
- 抽象工廠增強(qiáng)了程序的可擴(kuò)展性,當(dāng)增加一個(gè)新的產(chǎn)品族時(shí),不需要修改原代碼,滿足開(kāi)閉原則;
- 當(dāng)需要產(chǎn)品族時(shí),抽象工廠可以保證客戶端始終只使用同一個(gè)產(chǎn)品的產(chǎn)品組;
- 當(dāng)產(chǎn)品族中需要增加一個(gè)新的產(chǎn)品時(shí),所有的工廠類都需要進(jìn)行修改。
6.3 Kotlin 代碼實(shí)現(xiàn)
interface Plant
class OrangePlant : Plant
class ApplePlant : Plant
abstract class PlantFactory {
abstract fun makePlant(): Plant
companion object {
inline fun <reified T : Plant> createFactory(): PlantFactory = when (T::class) {
OrangePlant::class -> OrangeFactory()
ApplePlant::class -> AppleFactory()
else -> throw IllegalArgumentException()
}
}
}
class AppleFactory : PlantFactory() {
override fun makePlant(): Plant = ApplePlant()
}
class OrangeFactory : PlantFactory() {
override fun makePlant(): Plant = OrangePlant()
}
fun main() {
val plantFactory = PlantFactory.createFactory<OrangePlant>()
val plant = plantFactory.makePlant()
println("plant is: $plant")
}
7. Kotlin 實(shí)現(xiàn)裝飾器模式
7.1 模式的介紹
該模式定義了在不改變現(xiàn)有對(duì)象結(jié)構(gòu)的情況下,動(dòng)態(tài)地給該對(duì)象增加一些職責(zé)(即增加其額外功能)的模式,它屬于對(duì)象結(jié)構(gòu)型模式。
7.2 模式的特點(diǎn)
- 裝飾器是繼承的有力補(bǔ)充,比繼承靈活,在不改變?cè)袑?duì)象的情況下,動(dòng)態(tài)的給一個(gè)對(duì)象擴(kuò)展功能,即插即用;
- 通過(guò)使用不用裝飾類及這些裝飾類的排列組合,可以實(shí)現(xiàn)不同效果;
- 裝飾器模式完全遵守開(kāi)閉原則;
- 裝飾模式會(huì)增加許多子類,過(guò)度使用會(huì)增加程序得復(fù)雜性。
7.3 Kotlin 代碼實(shí)現(xiàn)
interface CoffeeMachine {
fun makeSmallCoffee()
fun makeLargeCoffee()
}
class NormalCoffeeMachine : CoffeeMachine {
override fun makeSmallCoffee() = println("makeSmallCoffee")
override fun makeLargeCoffee() = println("makeLargeCoffee")
}
class EnhancedCoffeeMachine(val coffeeMachine: CoffeeMachine) : CoffeeMachine by coffeeMachine {
override fun makeLargeCoffee() {
println("makeLargeCoffee")
coffeeMachine.makeLargeCoffee()
}
fun makeCoffeeWithMilk() {
println("makeCoffeeWithMilk")
coffeeMachine.makeSmallCoffee()
println("add something")
}
}
fun main() {
val normalMachine = NormalCoffeeMachine()
val enhancedMachine = EnhancedCoffeeMachine(normalMachine)
// 非重寫(xiě)行為
enhancedMachine.makeSmallCoffee()
// 重寫(xiě)行為
enhancedMachine.makeLargeCoffee()
// 繼承行為
enhancedMachine.makeCoffeeWithMilk()
}
8. Kotlin 實(shí)現(xiàn)適配器模式
8.1 模式的介紹
該模式定義了將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。適配器模式分為類結(jié)構(gòu)型模式和對(duì)象結(jié)構(gòu)型模式兩種,前者類之間的耦合度比后者高。
8.2 模式的特點(diǎn)
- 客戶端通過(guò)適配器可以透明地調(diào)用目標(biāo)接口;
- 復(fù)用了現(xiàn)存的類,開(kāi)發(fā)者不需要修改原有代碼而重用現(xiàn)有的適配者類;
- 將目標(biāo)類和適配者類解耦,解決了目標(biāo)類和適配者類接口不一致的問(wèn)題。
8.3 Kotlin 代碼實(shí)現(xiàn)
interface Temperature {
var temperature: Double
}
class CTemperature(override var temperature: Double) : Temperature
class FTemperature(var cTemperature: CTemperature) : Temperature {
override var temperature: Double
get() = convertCelsiusToFahrenheit(cTemperature.temperature)
set(temperatureInF) {
cTemperature.temperature = convertFahrenheitToCelsius(temperatureInF)
}
private fun convertFToCelsius(f: Double): Double = (f - 32) * 5 / 9
private fun convertCToFahrenheit(c: Double): Double = (c * 9 / 5) + 32
}
fun main() {
val cTemperature = CTemperature(0.0)
val fTemperature = FTemperature(celsiusTemperature)
cTemperature.temperature = 36.6
println("${cTemperature.temperature} C -> ${fTemperature.temperature} F")
fTemperature.temperature = 100.0
println("${fTemperature.temperature} F -> ${cTemperature.temperature} C")
}
9. 總結(jié)
到這里,有關(guān) Kotlin 在常用的設(shè)計(jì)模式中應(yīng)用都一一介紹完畢了,相信大家對(duì) Kotlin 理解和運(yùn)用有了更深的掌握。后面 Kotlin 應(yīng)用篇就比較偏泛化,比如說(shuō) Kotlin 用于 Android 開(kāi)發(fā)、iOS 開(kāi)發(fā)、Gradle 腳本開(kāi)發(fā)、服務(wù)端程序開(kāi)發(fā)、Web 開(kāi)發(fā)等。