服务端相关 / Kotlin 如何实现其他常用设计模式

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 开发等。