Kotlin 如何實現其他常用設計模式
前兩篇文章都詳細分析了 Kotlin 如何實現常用的單例、代理設計模式。并且從代碼實現和使用場景分別對比了 Java 和 Kotlin。那其實總共有 23 種設計模式,不可能每一種都能詳細介紹,那么這篇文章會繼續介紹 Kotlin 實現其他設計模式方法,因為篇幅有限,不會特別詳細對比 Java 中的實現。
1. Kotlin 實現觀察者模式
1.1 模式的介紹
模式定義了指多個對象間存在一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。這種模式有時又稱作發布 - 訂閱模式、模型 - 視圖模式,它是對象行為型模式。
1.2 模式的特點
- 降低了目標與觀察者之間的耦合關系,兩者之間是抽象耦合關系;
- 目標與觀察者之間建立了一套觸發機制;
- 當觀察者對象很多時,通知的發布會花費很多時間,影響程序的效率。
1.3 Kotlin 代碼實現
Kotlin 實現觀察者模式可以使用 Kotlin 中內置的 by Delegates.observable 代理輕松實現。
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 實現策略模式
2.1 模式的介紹
該模式定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。策略模式屬于對象行為模式,它通過對算法進行封裝,把使用算法的責任和算法的實現分割開來,并委派給不同的對象對這些算法進行管理。
2.2 模式的特點
- 使用策略模式可以避免使用多重條件語句,如 if…else 語句、switch…case 語句;
- 提供了一系列的可供重用的策略實現,適合使用繼承可以把公共代碼轉移到父類里面,從而避免重復的代碼;
- 提供了對開閉原則的完美支持,可以在不修改原代碼的情況下,靈活增加新策略實現;
- 策略模式會新增很多的策略類,增加維護難度。
2.3 Kotlin 代碼實現
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 實現狀態模式
3.1 模式的介紹
該模式定義了對有狀態的對象,把復雜的 “判斷邏輯” 提取到不同的狀態對象中,允許狀態對象在其內部狀態發生改變時改變其行為。
3.2 模式的特點
- 狀態類職責明確,有利于程序的擴展。通過定義新的子類很容易地增加新的狀態和轉換;
- 結構清晰,狀態模式將與特定狀態相關的行為局部化到一個狀態中,并且將不同狀態的行為分割開來,符合 “單一職責原則”;
- 不同的狀態引入獨立的對象中會使得狀態轉換變得更加明確,且減少對象間的相互依賴;
- 狀態模式的使用必然會增加系統的類與對象的個數。
3.3 Kotlin 代碼實現
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 實現 Builder 模式
4.1 模式的介紹
該模式定義了將一個復雜對象的構造與它的表示分離,使同樣的構建過程可以創建不同的表示。它是將一個復雜的對象分解為多個簡單的對象,然后一步一步構建而成。它將變與不變相分離,即產品的組成部分是不變的,但每一部分是可以靈活選擇的。
4.2 模式的特點
- 擴展性好,各個具體的建造者相互獨立,有利于解耦。
- 封裝性好,構建和表示分離。
- 隱藏產品內部組成的細節,可以對創建過程逐步細化,而不對其它模塊產生任何影響。
- 內部變化復雜,如果產品內部發生變化,則建造者也要同步修改,維護成本較大。
4.3 Kotlin 代碼實現
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 實現工廠方法模式
5.1 模式的介紹
該模式實際上是對簡單工廠模式的進一步抽象化,其好處是可以使系統在不修改原來代碼的情況下引進新的產品,即滿足開閉原則。
5.2 模式的特點
- 靈活性增強,對于新產品的創建,只需多寫一個相應的工廠類;
- 高度解耦。
5.3 Kotlin 代碼實現
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 實現抽象工廠模式
6.1 模式的介紹
該模式定義了一種為訪問類提供一個創建一組相關或相互依賴對象的接口,且訪問類無須指定所要產品的具體類就能得到同族的不同等級的產品的模式結構,它是工廠方法模式的升級版本,工廠方法模式只生產一個等級的產品,而抽象工廠模式可生產多個等級的產品。
6.2 模式的特點
- 可以在類的內部對產品族中相關聯的多等級產品共同管理,而不必專門引入多個新的類來進行管理;
- 抽象工廠增強了程序的可擴展性,當增加一個新的產品族時,不需要修改原代碼,滿足開閉原則;
- 當需要產品族時,抽象工廠可以保證客戶端始終只使用同一個產品的產品組;
- 當產品族中需要增加一個新的產品時,所有的工廠類都需要進行修改。
6.3 Kotlin 代碼實現
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 實現裝飾器模式
7.1 模式的介紹
該模式定義了在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式,它屬于對象結構型模式。
7.2 模式的特點
- 裝飾器是繼承的有力補充,比繼承靈活,在不改變原有對象的情況下,動態的給一個對象擴展功能,即插即用;
- 通過使用不用裝飾類及這些裝飾類的排列組合,可以實現不同效果;
- 裝飾器模式完全遵守開閉原則;
- 裝飾模式會增加許多子類,過度使用會增加程序得復雜性。
7.3 Kotlin 代碼實現
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)
// 非重寫行為
enhancedMachine.makeSmallCoffee()
// 重寫行為
enhancedMachine.makeLargeCoffee()
// 繼承行為
enhancedMachine.makeCoffeeWithMilk()
}
8. Kotlin 實現適配器模式
8.1 模式的介紹
該模式定義了將一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。適配器模式分為類結構型模式和對象結構型模式兩種,前者類之間的耦合度比后者高。
8.2 模式的特點
- 客戶端通過適配器可以透明地調用目標接口;
- 復用了現存的類,開發者不需要修改原有代碼而重用現有的適配者類;
- 將目標類和適配者類解耦,解決了目標類和適配者類接口不一致的問題。
8.3 Kotlin 代碼實現
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. 總結
到這里,有關 Kotlin 在常用的設計模式中應用都一一介紹完畢了,相信大家對 Kotlin 理解和運用有了更深的掌握。后面 Kotlin 應用篇就比較偏泛化,比如說 Kotlin 用于 Android 開發、iOS 開發、Gradle 腳本開發、服務端程序開發、Web 開發等。