Kotlin中GoF设计模式教程

设计模式是针对软件设计中常见问题的通用可重复解决方案。在这篇博文中,我们将深入研究各种设计模式,并探索如何在Kotlin中有效地实现它们。

建造者模式
Builder 设计模式用于通过将构造过程与实际表示分离来构造复杂对象。设计模式用于通过将构建过程与实际表示分离来构建复杂的对象。
当一个对象有大量参数,并且我们希望提供一种更具可读性和灵活的方式来构造它时,它特别有用。

下面是在 Kotlin 中实现 Builder 设计模式的示例:Kotlin 中的设计模式:

// Product class
data class Computer(
    val cpu: String,
    val ram: String,
    val storage: String,
    val graphicsCard: String
)

// Concrete builder class
class ComputerBuilder {
    private var cpu: String =
""
    private var ram: String =
""
    private var storage: String =
""
    private var graphicsCard: String =
""

    fun cpu(cpu: String): ComputerBuilder {
        this.cpu = cpu
        return this
    }

    fun ram(ram: String): ComputerBuilder {
        this.ram = ram
        return this
    }

    fun storage(storage: String): ComputerBuilder {
        this.storage = storage
        return this
    }

    fun graphicsCard(graphicsCard: String): ComputerBuilder {
        this.graphicsCard = graphicsCard
        return this
    }

    fun build(): Computer {
        return Computer(cpu, ram, storage, graphicsCard)
    }
}

fun main() {
   
// Build the computer with a specific configuration
    val builder = ComputerBuilder()
    val gamingComputer = builder
        .cpu(
"Intel Core i9")
        .ram(
"32GB DDR4")
        .storage(
"1TB SSD")
        .graphicsCard(
"NVIDIA RTX 3080")
        .build()
}

在这段代码中
  • 计算机Computer类作为要构建的产品,封装了 CPU、内存、存储器和显卡等属性。
  • 显卡ComputerBuilder 接口声明了用于配置每个属性的方法,而 ComputerBuilder 类则实现了该接口,并逐步设置数值。
  • 在主函数内的客户端代码中,ComputerBuilder 实例被用来通过方法链构建具有特定配置的计算机对象。

这种方法增强了可读性和灵活性,尤其是在处理具有大量可选或可互换组件的对象时,因为构建器模式有利于逐步构建过程。

请注意,Builder 模式在 Kotlin 中并不像在 Java 中那样常用,例如,因为 Kotlin 提供了命名参数,在构造函数中使用命名参数的效果与 Builder 非常相似:

fun main() {
     // Without using the Builder pattern
    val simpleComputer = Computer(
        cpu =
"Intel Core i5",
        ram =
"16GB DDR4",
        storage =
"512GB SSD",
        graphicsCard =
"NVIDIA GTX 1660"
    )
}

在构造函数中使用命名参数(如上面的示例)更能保证空值安全,因为它不接受空值,也不必像 Builder 示例中那样将值设置为空字符串("")。

单例模式
Singleton 设计模式确保一个类只有一个实例,并提供对该实例的全局访问点。使用它的每个地方都将使用相同的实例,从而减少内存使用并确保一致性。当只需要一个对象来协调整个系统的操作时,例如管理共享资源或控制单个控制点(例如,配置管理器或日志记录服务),它非常有用。该模式通常涉及私有构造函数、访问实例的方法以及仅在首次请求时创建实例的延迟初始化。

在 Kotlin 中,单例设计模式可以通过多种方式实现。以下是两种常见的方法。

对象声明
在 Kotlin 中实现 Singleton 最直接的方法是使用对象声明。对象声明定义一个单例类并同时创建它的实例。该实例是在第一次访问时延迟创建的。

下面是使用对象声明方法的代码示例:

object MySingleton {
    // Singleton properties and methods go here
    fun doSomething() {
        println(
"Singleton is doing something")
    }
}

要使用我们的单例:
MySingleton.doSomething()

伴随对象
另一种方法是在类中使用伴生对象。这种方法使我们能够更好地控制初始化过程,并且当我们需要执行一些额外的设置时可以使用它。

让我们看看如何使用伴随对象方法:

class MySingleton private constructor() {

    companion object {
        private val instance: MySingleton by lazy { MySingleton() }

        fun getInstance(): MySingleton {
            return instance
        }
    }

    // Singleton properties and methods go here

    fun doSomething() {
        println(
"Singleton is doing something")
    }
}

使用单例:

val singletonInstance = MySingleton.getInstance()
singletonInstance.doSomething()

通过使用by lazy,instance仅在首次访问时创建 ,使其成为延迟初始化的单例。


适配器模式
适配器设计模式允许将现有类的接口用作另一个接口。它通常用于使现有类与其他类一起工作,而无需修改其源代码。

在 Kotlin 中,我们可以使用基于类或基于对象的适配器来实现适配器模式。

以下是基于类的适配器模式的示例:

// Target interface that the client expects
interface Printer {
    fun print()
}

// Adaptee (the class to be adapted)
class ModernPrinter {
    fun startPrint() {
        println(
"Printing in a modern way")
    }
}

// Class-based Adapter
class ModernPrinterAdapter(private val modernPrinter: ModernPrinter) : Printer {
    override fun print() {
        modernPrinter.startPrint()
    }
}

// Client code
fun main() {
    val modernPrinter = ModernPrinter()
    val legacyPrinter: Printer = ModernPrinterAdapter(modernPrinter)

    legacyPrinter.print()
}

在这个例子中:

  • Printer是客户端期望的目标接口。
  • ModernPrinter是要适配的类(Adaptee)。
  • ModernPrinterAdapter是基于类的适配器,用于适应ModernPrinter接口Printer。

装饰模式
装饰器设计模式允许静态或动态地将行为添加到单个对象,而不影响同一类中其他对象的行为。在 Kotlin 中,我们可以使用接口和类来实现装饰器模式。

下面是 Kotlin 中装饰器模式的一个简单示例:

// Component interface
interface Car {
    fun drive()
}

// Concrete component
class BasicCar : Car {
    override fun drive() {
        println(
"Move from A to B")
    }
}

// Extension function for Car interface
fun Car.decorate(initialize: () -> Unit): Car {
    return object : Car {
        override fun drive() {
            initialize()
            this@decorate.drive()
        }
    }
}

fun main() {
   
// Create a basic car
    val myBasicCar: Car = BasicCar()

   
// Decorate it to make it an offroad car
    val offroadCar: Car = myBasicCar.decorate {
        println(
"Configure offroad driving mode")
    }

   
// Drive the offroad car
    offroadCar.drive()
}

本例中,decorate将扩展功能添加到Car接口中。此扩展函数采用名为 的 lambda 参数initialize,它表示要添加的附加行为。它返回一个新实例,Car该实例在调用原始方法之前包含指定的行为drive。

在该main功能中,使用扩展功能对基本车进行装饰decorate,创建越野车,然后驾驶越野车。

此代码示例的输出将是:

Configure offroad driving mode
Move from A to B

门面模式
Facade 设计模式为子系统中的一组接口提供了简化的接口,使其更易于使用。它涉及创建一个代表更高级别的统一接口的类,使客户端更容易与子系统交互。这可以通过提供单一入口点来帮助简化复杂系统的使用。

让我们用 Kotlin 创建一个简单的 Facade 模式示例。考虑一个具有多个类的子系统,这些类处理计算机系统、CPU、内存和硬盘驱动器的不同方面。

我们将创建一个 ComputerFacade 类来为客户端与子系统交互提供一个简单的接口:

// Subsystem classes
class CPU {
   fun processData() {
       println(
"Processing data...")
   }
}

class Memory {
   fun load() {
       println(
"Loading data into memory...")
   }
}

class HardDrive {
   fun readData() {
       println(
"Reading data from hard drive...")
   }
}

// Facade class
class ComputerFacade(
   private val cpu: CPU,
   private val memory: Memory,
   private val hardDrive: HardDrive
) {
   fun start() {
       println(
"ComputerFacade starting...")
       cpu.processData()
       memory.load()
       hardDrive.readData()
       println(
"ComputerFacade started successfully.")
   }
}

// Client code
fun main() {
   
// Create subsystem components
   val cpu = CPU()
   val memory = Memory()
   val hardDrive = HardDrive()

   
// Create facade and pass subsystem components to it
   val computerFacade = ComputerFacade(cpu, memory, hardDrive)

   
// Client interacts with the subsystem through the facade
   computerFacade.start()
}

在此示例中,该类ComputerFacade充当启动计算机系统的简化接口。客户端通过 与子系统(CPU、内存和硬盘)交互,ComputerFacade无需了解每个子系统组件的详细信息。

通过使用Facade模式,子系统的复杂性对客户端隐藏起来,客户端可以通过Facade提供的更直接、统一的接口与系统进行交互。这在处理大型复杂系统时特别有用。

观察者模式
观察者设计模式是一种行为设计模式,其中一个对象(称为主体)维护其依赖项(称为观察者)的列表,这些依赖者会收到任何状态更改的通知。该模式通常用于实现分布式事件处理系统。

这是一个简单的例子:

// Define an interface for the observer
interface Observer {
   fun update(value: Int)
}

// Define a concrete observer that implements the Observer interface
class ValueObserver(private val name: String) : Observer {
   override fun update(value: Int) {
       println(
"$name received value: $value")
   }
}

// Define a subject that emits values and notifies observers
class ValueSubject {
   private val observers = mutableListOf<Observer>()

   fun addObserver(observer: Observer) {
       observers.add(observer)
   }

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

   private val observable: Flow<Int> = flow {
       while (true) {
           emit(Random.nextInt(0..1000))
           delay(100)
       }
   }

   fun startObserving() {
       val observerJob = coroutineScope.launch {
           observable.collect { value ->
               notifyObservers(value)
           }
       }
   }

   private fun notifyObservers(value: Int) {
       for (observer in observers) {
           observer.update(value)
       }
   }
}

总之,这段代码建立了一个系统,多个观察者可以附加到一个主题ValueSubject。主体以连续流的形式发出随机值,ValueObserver每当发出新值时,每个连接的观察者都会收到通知。然后观察者打印一条消息,表明它收到了新值。


策略模式
策略设计模式是一种行为设计模式,它定义了一系列算法,封装了每个算法并使它们可以互换。它允许客户端在运行时从一系列算法中选择一种算法,而无需修改客户端代码。

这是一个例子:

// Define the strategy interface
interface PaymentStrategy {
   fun pay(amount: Double)
}

// Concrete implementation of a payment strategy: Credit Card
class CreditCardPaymentStrategy(private val cardNumber: String,
                                private val expiryDate: String,
                                private val cvv: String)
: PaymentStrategy {
   override fun pay(amount: Double) {
       
// Logic for credit card payment
       println(
"Paid $amount using credit card $cardNumber")
   }
}

// Concrete implementation of a payment strategy: PayPal
class PayPalPaymentStrategy(private val email: String) : PaymentStrategy {
   override fun pay(amount: Double) {
       
// Logic for PayPal payment
       println(
"Paid $amount using PayPal with email $email")
   }
}

// Context class that uses the strategy
class ShoppingCart(private val paymentStrategy: PaymentStrategy) {
   fun checkout(amount: Double) {
       paymentStrategy.pay(amount)
   }
}

fun main() {
   
// Client code
   val creditCardStrategy = CreditCardPaymentStrategy(
"1234-5678-9012-3456", "12/24", "123")
   val payPalStrategy = PayPalPaymentStrategy(
"john.doe@example.com")

   val shoppingCart1 = ShoppingCart(creditCardStrategy)
   val shoppingCart2 = ShoppingCart(payPalStrategy)

   shoppingCart1.checkout(100.0)
   shoppingCart2.checkout(50.0)
}

在这个例子中,PaymentStrategy接口定义了支付策略的合约,并且CreditCardPaymentStrategy是PayPalPaymentStrategy该策略的具体实现。该类ShoppingCart表示使用所选支付策略的上下文。

通过使用策略设计模式,我们可以轻松添加新的支付策略,而无需修改现有代码。我们可以创建实现该PaymentStrategy接口的新类,并在上下文中互换使用它们ShoppingCart。


工厂设计模式
工厂设计模式是一种创建模式,它提供了在超类中创建对象的接口,但允许子类更改将创建的对象的类型。当类无法预测它必须创建的对象的类时,通常会使用此模式。

下面是 Kotlin 中简单工厂设计模式的示例:

// Product interface
interface Product {
    fun create(): String
}

// Concrete Product A
class ConcreteProductA : Product {
    override fun create(): String {
        return
"Product A"
    }
}

// Concrete Product B
class ConcreteProductB : Product {
    override fun create(): String {
        return
"Product B"
    }
}

// Factory interface
interface ProductFactory {
    fun createProduct(): Product
}

// Concrete Factory A
class ConcreteFactoryA : ProductFactory {
    override fun createProduct(): Product {
        return ConcreteProductA()
    }
}

// Concrete Factory B
class ConcreteFactoryB : ProductFactory {
    override fun createProduct(): Product {
        return ConcreteProductB()
    }
}

// Client code
fun main() {
    val factoryA: ProductFactory = ConcreteFactoryA()
    val productA: Product = factoryA.createProduct()
    println(productA.create())

    val factoryB: ProductFactory = ConcreteFactoryB()
    val productB: Product = factoryB.createProduct()
    println(productB.create())
}

在此示例中,我们有一个Product代表要创建的产品的接口。
我们有两个具体的产品类ConcreteProductA和ConcreteProductB,它们实现了 Product 接口。
我们还有一个带有ProductFactory方法的接口createProduct()和两个具体工厂类,
ConcreteFactoryA它们ConcreteFactoryB实现该接口并返回相应具体产品的实例。

抽象工厂模式
抽象工厂设计模式提供了一个接口,用于创建相关或依赖对象系列,而无需指定它们的具体类。当系统需要独立于其对象的创建、组合、表示方式,并且客户端代码应该与多个对象系列一起工作时,通常会使用这种模式。在 Kotlin 中,您可以使用接口、抽象类来实现抽象设计模式,和具体的类。

让我们看一个简单的例子来说明抽象设计模式:

// Abstract Product A
interface ProductA {
    fun operationA(): String
}

// Concrete Product A1
class ConcreteProductA1 : ProductA {
    override fun operationA(): String {
        return
"Product A1"
    }
}

// Concrete Product A2
class ConcreteProductA2 : ProductA {
    override fun operationA(): String {
        return
"Product A2"
    }
}

// Abstract Product B
interface ProductB {
    fun operationB(): String
}

// Concrete Product B1
class ConcreteProductB1 : ProductB {
    override fun operationB(): String {
        return
"Product B1"
    }
}

// Concrete Product B2
class ConcreteProductB2 : ProductB {
    override fun operationB(): String {
        return
"Product B2"
    }
}

// Abstract Factory
interface AbstractFactory {
    fun createProductA(): ProductA
    fun createProductB(): ProductB
}

// Concrete Factory 1
class ConcreteFactory1 : AbstractFactory {
    override fun createProductA(): ProductA {
        return ConcreteProductA1()
    }

    override fun createProductB(): ProductB {
        return ConcreteProductB1()
    }
}

// Concrete Factory 2
class ConcreteFactory2 : AbstractFactory {
    override fun createProductA(): ProductA {
        return ConcreteProductA2()
    }

    override fun createProductB(): ProductB {
        return ConcreteProductB2()
    }
}

// Client Code
fun main() {
    val factory1: AbstractFactory = ConcreteFactory1()
    val productA1: ProductA = factory1.createProductA()
    val productB1: ProductB = factory1.createProductB()

    println(productA1.operationA())
// Output: Product A1
    println(productB1.operationB())
// Output: Product B1

    val factory2: AbstractFactory = ConcreteFactory2()
    val productA2: ProductA = factory2.createProductA()
    val productB2: ProductB = factory2.createProductB()

    println(productA2.operationA())
// Output: Product A2
    println(productB2.operationB())
// Output: Product B2
}

在本例中,AbstractFactory 声明了 ProductA 和 ProductB 两种类型产品的创建方法。具体工厂 ConcreteFactory1 和 ConcreteFactory2 实现了这些创建方法,以生成具体的产品 ConcreteProductA1、ConcreteProductA2、ConcreteProductB1 和 ConcreteProductB2。这样,客户端代码就可以使用特定的工厂来创建产品,而无需知道这些产品的具体类。

这种结构允许通过引入新产品和工厂来轻松扩展系统,而无需修改现有的客户端代码。

抽象工厂模式与工厂模式的主要区别

  • 工厂模式使用继承并依赖子类来处理对象创建,允许类将实例化委托给其子类。
  • 抽象工厂模式使用对象组合,为创建相关或依赖对象的族提供了接口。它涉及多个工厂方法,每个工厂方法负责创建族中不同类型的对象。
  • 工厂模式创建一个产品,而抽象工厂模式创建相关产品系列。
  • 在工厂模式中,客户代码使用具体的创建类,并依靠多态性来实例化产品。
  • 而在抽象工厂模式中,客户代码使用抽象工厂来创建产品系列,并且它是为多个产品系列而设计的。

结论
在本文中,我们了解了什么是 Kotlin 中的设计模式、设计模式在软件开发过程中的优势以及 Kotlin 提供的各种设计模式。