MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Kotlin设计模式实践

2024-10-214.2k 阅读

单例模式

在软件开发中,单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在 Kotlin 中实现单例模式有多种方式。

饿汉式单例

饿汉式单例在类加载时就创建实例,这种方式简单直接,并且线程安全。

class EagerSingleton {
    companion object {
        val instance: EagerSingleton by lazy { EagerSingleton() }
    }
}

这里使用 by lazy 来实现延迟初始化,by lazy 是 Kotlin 提供的一种便捷方式,它会在第一次访问 instance 时才创建 EagerSingleton 的实例。

懒汉式单例

懒汉式单例在第一次使用时才创建实例。在多线程环境下,如果不进行适当的同步处理,可能会创建多个实例。

class LazySingleton {
    companion object {
        private var instance: LazySingleton? = null

        fun getInstance(): LazySingleton {
            if (instance == null) {
                synchronized(this) {
                    if (instance == null) {
                        instance = LazySingleton()
                    }
                }
            }
            return instance!!
        }
    }
}

在上述代码中,使用 synchronized 关键字来确保在多线程环境下只有一个实例被创建。双重检查锁定机制(if (instance == null) 的两次检查)提高了性能,因为在实例已经创建后,不需要每次都进入同步块。

枚举单例

Kotlin 中的枚举类型提供了一种简洁且线程安全的单例实现方式。

enum class EnumSingleton {
    INSTANCE;
    // 可以在这里添加方法和属性
    fun doSomething() {
        println("Doing something in EnumSingleton")
    }
}

枚举类型在 JVM 中天然就是线程安全的,并且只会被实例化一次。通过 EnumSingleton.INSTANCE 即可访问单例实例。

工厂模式

工厂模式提供了一种创建对象的方式,将对象的创建和使用分离。这种模式使得代码更加可维护和可扩展。

简单工厂模式

简单工厂模式定义了一个工厂类,用于创建产品对象。

abstract class Shape {
    abstract fun draw()
}

class Circle : Shape() {
    override fun draw() {
        println("Drawing a circle")
    }
}

class Rectangle : Shape() {
    override fun draw() {
        println("Drawing a rectangle")
    }
}

class ShapeFactory {
    fun createShape(shapeType: String): Shape? {
        return when (shapeType.lowercase()) {
            "circle" -> Circle()
            "rectangle" -> Rectangle()
            else -> null
        }
    }
}

使用时:

val factory = ShapeFactory()
val circle = factory.createShape("circle")
circle?.draw()

简单工厂模式的优点是实现简单,缺点是不符合开闭原则,如果要添加新的形状,需要修改 ShapeFactorycreateShape 方法。

工厂方法模式

工厂方法模式将对象的创建延迟到子类中,每个子类负责创建特定的产品对象。

abstract class ShapeFactory {
    abstract fun createShape(): Shape
}

class CircleFactory : ShapeFactory() {
    override fun createShape(): Shape {
        return Circle()
    }
}

class RectangleFactory : ShapeFactory() {
    override fun createShape(): Shape {
        return Rectangle()
    }
}

使用时:

val circleFactory = CircleFactory()
val circle = circleFactory.createShape()
circle.draw()

工厂方法模式符合开闭原则,当需要添加新的形状时,只需要创建新的工厂子类,而不需要修改现有代码。

抽象工厂模式

抽象工厂模式提供了一个创建一系列相关或依赖对象的接口,而无需指定它们具体的类。

abstract class AbstractProductA {
    abstract fun interact(productB: AbstractProductB)
}

abstract class AbstractProductB {
    abstract fun usefulFunctionB()
}

class ProductA1 : AbstractProductA() {
    override fun interact(productB: AbstractProductB) {
        val result = productB.usefulFunctionB()
        println("The result of the interaction between A1 and $result")
    }
}

class ProductB1 : AbstractProductB() {
    override fun usefulFunctionB() = "B1"
}

class ProductA2 : AbstractProductA() {
    override fun interact(productB: AbstractProductB) {
        val result = productB.usefulFunctionB()
        println("The result of the interaction between A2 and $result")
    }
}

class ProductB2 : AbstractProductB() {
    override fun usefulFunctionB() = "B2"
}

abstract class AbstractFactory {
    abstract fun createProductA(): AbstractProductA
    abstract fun createProductB(): AbstractProductB
}

class ConcreteFactory1 : AbstractFactory() {
    override fun createProductA(): AbstractProductA {
        return ProductA1()
    }

    override fun createProductB(): AbstractProductB {
        return ProductB1()
    }
}

class ConcreteFactory2 : AbstractFactory() {
    override fun createProductA(): AbstractProductA {
        return ProductA2()
    }

    override fun createProductB(): AbstractProductB {
        return ProductB2()
    }
}

使用时:

val factory1 = ConcreteFactory1()
val productA1 = factory1.createProductA()
val productB1 = factory1.createProductB()
productA1.interact(productB1)

抽象工厂模式适用于创建一系列相关对象的场景,它提供了更高层次的抽象和封装。

策略模式

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。

interface PaymentStrategy {
    fun pay(amount: Double)
}

class CreditCardPayment : PaymentStrategy {
    private val cardNumber: String
    private val cvv: String
    private val expirationDate: String

    constructor(cardNumber: String, cvv: String, expirationDate: String) {
        this.cardNumber = cardNumber
        this.cvv = cvv
        this.expirationDate = expirationDate
    }

    override fun pay(amount: Double) {
        println("Paying $amount with credit card $cardNumber")
    }
}

class PayPalPayment : PaymentStrategy {
    private val email: String

    constructor(email: String) {
        this.email = email
    }

    override fun pay(amount: Double) {
        println("Paying $amount with PayPal account $email")
    }
}

class ShoppingCart {
    private val items: MutableList<Double> = mutableListOf()

    fun addItem(price: Double) {
        items.add(price)
    }

    fun calculateTotal(): Double {
        return items.sum()
    }

    fun checkout(paymentStrategy: PaymentStrategy) {
        val amount = calculateTotal()
        paymentStrategy.pay(amount)
    }
}

使用时:

val cart = ShoppingCart()
cart.addItem(10.0)
cart.addItem(20.0)
val creditCardPayment = CreditCardPayment("1234567890123456", "123", "12/25")
cart.checkout(creditCardPayment)

val paypalPayment = PayPalPayment("example@example.com")
cart.checkout(paypalPayment)

策略模式使得算法的切换更加灵活,符合开闭原则,当需要添加新的支付策略时,只需要创建新的实现类并实现 PaymentStrategy 接口。

观察者模式

观察者模式定义了一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都会收到通知并自动更新。

interface Observer {
    fun update(subject: Subject)
}

interface Subject {
    fun registerObserver(observer: Observer)
    fun removeObserver(observer: Observer)
    fun notifyObservers()
}

class WeatherData : Subject {
    private val observers: MutableList<Observer> = mutableListOf()
    private var temperature: Double = 0.0
    private var humidity: Double = 0.0
    private var pressure: Double = 0.0

    override fun registerObserver(observer: Observer) {
        observers.add(observer)
    }

    override fun removeObserver(observer: Observer) {
        observers.remove(observer)
    }

    override fun notifyObservers() {
        observers.forEach { it.update(this) }
    }

    fun measurementsChanged() {
        notifyObservers()
    }

    fun setMeasurements(temperature: Double, humidity: Double, pressure: Double) {
        this.temperature = temperature
        this.humidity = humidity
        this.pressure = pressure
        measurementsChanged()
    }

    fun getTemperature() = temperature
    fun getHumidity() = humidity
    fun getPressure() = pressure
}

class CurrentConditionsDisplay : Observer {
    private var temperature: Double = 0.0
    private var humidity: Double = 0.0
    private val weatherData: WeatherData

    constructor(weatherData: WeatherData) {
        this.weatherData = weatherData
        weatherData.registerObserver(this)
    }

    override fun update(subject: Subject) {
        if (subject is WeatherData) {
            this.temperature = subject.getTemperature()
            this.humidity = subject.getHumidity()
            display()
        }
    }

    fun display() {
        println("Current conditions: $temperature°F degrees and $humidity% humidity")
    }
}

使用时:

val weatherData = WeatherData()
val currentConditionsDisplay = CurrentConditionsDisplay(weatherData)
weatherData.setMeasurements(80.0, 65.0, 30.4)

在上述代码中,WeatherData 是主题,CurrentConditionsDisplay 是观察者。当 WeatherData 的测量数据发生变化时,会通知所有注册的观察者,CurrentConditionsDisplay 接收到通知后更新并显示最新的数据。

装饰器模式

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。

interface Beverage {
    fun getDescription(): String
    fun cost(): Double
}

class Espresso : Beverage {
    override fun getDescription() = "Espresso"
    override fun cost() = 1.99
}

class DarkRoast : Beverage {
    override fun getDescription() = "Dark Roast"
    override fun cost() = 0.99
}

abstract class CondimentDecorator : Beverage {
    protected lateinit var beverage: Beverage
}

class Mocha : CondimentDecorator {
    constructor(beverage: Beverage) {
        this.beverage = beverage
    }

    override fun getDescription() = "${beverage.getDescription()}, Mocha"
    override fun cost() = 0.20 + beverage.cost()
}

class Whip : CondimentDecorator {
    constructor(beverage: Beverage) {
        this.beverage = beverage
    }

    override fun getDescription() = "${beverage.getDescription()}, Whip"
    override fun cost() = 0.10 + beverage.cost()
}

使用时:

val espresso = Espresso()
println("Description: ${espresso.getDescription()}, Cost: ${espresso.cost()}")

val darkRoastWithMochaAndWhip = Whip(Mocha(DarkRoast()))
println("Description: ${darkRoastWithMochaAndWhip.getDescription()}, Cost: ${darkRoastWithMochaAndWhip.cost()}")

在这个例子中,EspressoDarkRoast 是具体的饮料,MochaWhip 是装饰器。通过装饰器可以为饮料添加新的配料,同时保持饮料类的结构不变。

代理模式

代理模式为其他对象提供一种代理以控制对这个对象的访问。

interface Image {
    fun display()
}

class RealImage : Image {
    private val fileName: String

    constructor(fileName: String) {
        this.fileName = fileName
        loadFromDisk()
    }

    private fun loadFromDisk() {
        println("Loading $fileName from disk")
    }

    override fun display() {
        println("Displaying $fileName")
    }
}

class ProxyImage : Image {
    private val realImage: RealImage?
    private val fileName: String

    constructor(fileName: String) {
        this.fileName = fileName
        realImage = null
    }

    override fun display() {
        if (realImage == null) {
            RealImage(fileName).display()
        } else {
            realImage.display()
        }
    }
}

使用时:

val proxyImage = ProxyImage("test.jpg")
proxyImage.display()

在上述代码中,ProxyImage 代理了 RealImage。当调用 proxyImage.display() 时,如果 RealImage 还未创建,则创建并显示图像,这样可以在需要时才加载实际的图像,节省资源。

适配器模式

适配器模式将一个类的接口转换成客户希望的另一个接口。

interface Target {
    fun request()
}

class Adaptee {
    fun specificRequest() {
        println("Called specificRequest()")
    }
}

class Adapter : Target {
    private val adaptee: Adaptee

    constructor(adaptee: Adaptee) {
        this.adaptee = adaptee
    }

    override fun request() {
        adaptee.specificRequest()
    }
}

使用时:

val adaptee = Adaptee()
val target = Adapter(adaptee)
target.request()

这里 Adaptee 有一个特殊的方法 specificRequest,而客户需要的是 Target 接口的 request 方法。Adapter 类将 Adaptee 的接口适配成 Target 的接口,使得客户可以通过 Target 接口调用 Adaptee 的功能。

模板方法模式

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。

abstract class AbstractClass {
    final fun templateMethod() {
        primitiveOperation1()
        primitiveOperation2()
        concreteOperation()
        hook()
    }

    protected abstract fun primitiveOperation1()
    protected abstract fun primitiveOperation2()

    protected fun concreteOperation() {
        println("Concrete operation in AbstractClass")
    }

    protected fun hook() {}
}

class ConcreteClass : AbstractClass() {
    override fun primitiveOperation1() {
        println("Primitive operation 1 implementation in ConcreteClass")
    }

    override fun primitiveOperation2() {
        println("Primitive operation 2 implementation in ConcreteClass")
    }

    override fun hook() {
        println("Hook method implementation in ConcreteClass")
    }
}

使用时:

val concreteClass = ConcreteClass()
concreteClass.templateMethod()

AbstractClass 中,templateMethod 定义了算法的骨架,包含了一些抽象的和具体的方法调用。ConcreteClass 继承自 AbstractClass 并实现了抽象方法,还可以选择性地重写钩子方法 hook

责任链模式

责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

abstract class AbstractLogger {
    companion object {
        const val INFO = 1
        const val DEBUG = 2
        const val ERROR = 3
    }

    protected var level: Int = 0
    protected var nextLogger: AbstractLogger? = null

    fun setNext(logger: AbstractLogger) {
        this.nextLogger = logger
    }

    fun logMessage(level: Int, message: String) {
        if (this.level <= level) {
            write(message)
        }
        nextLogger?.logMessage(level, message)
    }

    protected abstract fun write(message: String)
}

class ConsoleLogger : AbstractLogger() {
    constructor(level: Int) {
        this.level = level
    }

    override fun write(message: String) {
        println("Standard Console::Logger: $message")
    }
}

class ErrorLogger : AbstractLogger() {
    constructor(level: Int) {
        this.level = level
    }

    override fun write(message: String) {
        println("Error Console::Logger: $message")
    }
}

class FileLogger : AbstractLogger() {
    constructor(level: Int) {
        this.level = level
    }

    override fun write(message: String) {
        println("File::Logger: $message")
    }
}

使用时:

val errorLogger = ErrorLogger(AbstractLogger.ERROR)
val fileLogger = FileLogger(AbstractLogger.DEBUG)
val consoleLogger = ConsoleLogger(AbstractLogger.INFO)

errorLogger.setNext(fileLogger)
fileLogger.setNext(consoleLogger)

val logMessage = "This is an information log"
errorLogger.logMessage(AbstractLogger.INFO, logMessage)

在这个例子中,ErrorLoggerFileLoggerConsoleLogger 形成了一条责任链。当调用 errorLogger.logMessage 时,它会根据日志级别决定是否处理该消息,如果不处理则传递给下一个日志记录器。

命令模式

命令模式将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

interface Command {
    fun execute()
}

class Light {
    fun on() {
        println("Light is on")
    }

    fun off() {
        println("Light is off")
    }
}

class LightOnCommand : Command {
    private val light: Light

    constructor(light: Light) {
        this.light = light
    }

    override fun execute() {
        light.on()
    }
}

class LightOffCommand : Command {
    private val light: Light

    constructor(light: Light) {
        this.light = light
    }

    override fun execute() {
        light.off()
    }
}

class RemoteControl {
    private val onCommands: Array<Command?>
    private val offCommands: Array<Command?>

    constructor() {
        onCommands = arrayOfNulls(7)
        offCommands = arrayOfNulls(7)
        val noCommand: Command = object : Command {
            override fun execute() {}
        }
        for (i in 0 until 7) {
            onCommands[i] = noCommand
            offCommands[i] = noCommand
        }
    }

    fun setCommand(slot: Int, onCommand: Command, offCommand: Command) {
        onCommands[slot] = onCommand
        offCommands[slot] = offCommand
    }

    fun onButtonWasPushed(slot: Int) {
        onCommands[slot]?.execute()
    }

    fun offButtonWasPushed(slot: Int) {
        offCommands[slot]?.execute()
    }
}

使用时:

val light = Light()
val lightOn = LightOnCommand(light)
val lightOff = LightOffCommand(light)

val remoteControl = RemoteControl()
remoteControl.setCommand(0, lightOn, lightOff)

remoteControl.onButtonWasPushed(0)
remoteControl.offButtonWasPushed(0)

在这个例子中,LightOnCommandLightOffCommand 封装了对 Light 对象的操作。RemoteControl 通过持有这些命令对象,可以根据用户操作调用相应的命令来控制 Light

状态模式

状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

interface State {
    fun insertQuarter()
    fun ejectQuarter()
    fun turnCrank()
    fun dispense()
}

class NoQuarterState : State {
    private val gumballMachine: GumballMachine

    constructor(gumballMachine: GumballMachine) {
        this.gumballMachine = gumballMachine
    }

    override fun insertQuarter() {
        println("You inserted a quarter")
        gumballMachine.setState(gumballMachine.hasQuarterState)
    }

    override fun ejectQuarter() {
        println("You haven't inserted a quarter")
    }

    override fun turnCrank() {
        println("You turned, but there's no quarter")
    }

    override fun dispense() {
        println("You need to pay first")
    }
}

class HasQuarterState : State {
    private val gumballMachine: GumballMachine

    constructor(gumballMachine: GumballMachine) {
        this.gumballMachine = gumballMachine
    }

    override fun insertQuarter() {
        println("You can't insert another quarter")
    }

    override fun ejectQuarter() {
        println("Quarter returned")
        gumballMachine.setState(gumballMachine.noQuarterState)
    }

    override fun turnCrank() {
        println("You turned...")
        gumballMachine.setState(gumballMachine.soldState)
    }

    override fun dispense() {
        println("No gumball dispensed")
    }
}

class SoldState : State {
    private val gumballMachine: GumballMachine

    constructor(gumballMachine: GumballMachine) {
        this.gumballMachine = gumballMachine
    }

    override fun insertQuarter() {
        println("Please wait, we're already giving you a gumball")
    }

    override fun ejectQuarter() {
        println("Sorry, you already turned the crank")
    }

    override fun turnCrank() {
        println("Turning twice doesn't get you another gumball!")
    }

    override fun dispense() {
        gumballMachine.releaseBall()
        if (gumballMachine.count > 0) {
            gumballMachine.setState(gumballMachine.noQuarterState)
        } else {
            println("Oops, out of gumballs!")
            gumballMachine.setState(gumballMachine.soldOutState)
        }
    }
}

class SoldOutState : State {
    private val gumballMachine: GumballMachine

    constructor(gumballMachine: GumballMachine) {
        this.gumballMachine = gumballMachine
    }

    override fun insertQuarter() {
        println("You can't insert a quarter, the machine is sold out")
    }

    override fun ejectQuarter() {
        println("You can't eject, you haven't inserted a quarter yet")
    }

    override fun turnCrank() {
        println("You turned, but there are no gumballs")
    }

    override fun dispense() {
        println("No gumball dispensed")
    }
}

class GumballMachine {
    var state: State
    var count: Int = 0

    val soldOutState: State
    val noQuarterState: State
    val hasQuarterState: State
    val soldState: State

    constructor(count: Int) {
        this.count = count
        soldOutState = SoldOutState(this)
        noQuarterState = NoQuarterState(this)
        hasQuarterState = HasQuarterState(this)
        soldState = SoldState(this)

        if (count > 0) {
            state = noQuarterState
        } else {
            state = soldOutState
        }
    }

    fun setState(state: State) {
        this.state = state
    }

    fun releaseBall() {
        println("A gumball comes rolling out the slot...")
        if (count != 0) {
            count--
        }
    }

    fun insertQuarter() {
        state.insertQuarter()
    }

    fun ejectQuarter() {
        state.ejectQuarter()
    }

    fun turnCrank() {
        state.turnCrank()
        state.dispense()
    }
}

使用时:

val gumballMachine = GumballMachine(5)
gumballMachine.insertQuarter()
gumballMachine.turnCrank()

在这个例子中,GumballMachine 的状态由 State 接口的不同实现类表示,如 NoQuarterStateHasQuarterState 等。根据机器的不同状态,对用户操作的响应也不同,并且状态之间可以相互转换。

组合模式

组合模式将对象组合成树形结构以表示“部分 - 整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

abstract class Component {
    protected val name: String

    constructor(name: String) {
        this.name = name
    }

    open fun add(component: Component) {
        throw UnsupportedOperationException()
    }

    open fun remove(component: Component) {
        throw UnsupportedOperationException()
    }

    open fun display(depth: Int) {
        throw UnsupportedOperationException()
    }
}

class Leaf : Component {
    constructor(name: String) : super(name)

    override fun display(depth: Int) {
        println("${" ".repeat(depth)}Leaf: $name")
    }
}

class Composite : Component {
    private val children: MutableList<Component> = mutableListOf()

    constructor(name: String) : super(name)

    override fun add(component: Component) {
        children.add(component)
    }

    override fun remove(component: Component) {
        children.remove(component)
    }

    override fun display(depth: Int) {
        println("${" ".repeat(depth)}Composite: $name")
        children.forEach { it.display(depth + 2) }
    }
}

使用时:

val root = Composite("root")
root.add(Leaf("Leaf A"))
root.add(Leaf("Leaf B"))

val comp = Composite("Composite X")
comp.add(Leaf("Leaf XA"))
comp.add(Leaf("Leaf XB"))

root.add(comp)

root.display(1)

在上述代码中,Leaf 代表叶子节点,Composite 代表组合节点。Composite 可以包含多个 Component(包括 Leaf 和其他 Composite),通过递归调用 display 方法可以展示整个树形结构。

享元模式

享元模式运用共享技术有效地支持大量细粒度的对象。

interface Flyweight {
    fun operation(extrinsicState: String)
}

class ConcreteFlyweight : Flyweight {
    private val intrinsicState: String

    constructor(intrinsicState: String) {
        this.intrinsicState = intrinsicState
    }

    override fun operation(extrinsicState: String) {
        println("ConcreteFlyweight: Intrinsic State = $intrinsicState, Extrinsic State = $extrinsicState")
    }
}

class FlyweightFactory {
    private val flyweights: MutableMap<String, Flyweight> = mutableMapOf()

    fun getFlyweight(intrinsicState: String): Flyweight {
        return flyweights.getOrPut(intrinsicState) { ConcreteFlyweight(intrinsicState) }
    }
}

使用时:

val factory = FlyweightFactory()

val flyweight1 = factory.getFlyweight("A")
flyweight1.operation("Extrinsic State 1")

val flyweight2 = factory.getFlyweight("A")
flyweight2.operation("Extrinsic State 2")

在这个例子中,FlyweightFactory 负责创建和管理享元对象。通过共享相同内在状态的对象,减少了内存消耗,提高了系统性能。

通过以上对各种设计模式在 Kotlin 中的实践介绍,我们可以看到不同设计模式在解决不同问题场景时的优势和特点。在实际的 Kotlin 项目开发中,根据具体需求合理选择和应用设计模式,可以提高代码的可维护性、可扩展性和复用性。