Kotlin设计模式实践
单例模式
在软件开发中,单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在 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()
简单工厂模式的优点是实现简单,缺点是不符合开闭原则,如果要添加新的形状,需要修改 ShapeFactory
的 createShape
方法。
工厂方法模式
工厂方法模式将对象的创建延迟到子类中,每个子类负责创建特定的产品对象。
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()}")
在这个例子中,Espresso
和 DarkRoast
是具体的饮料,Mocha
和 Whip
是装饰器。通过装饰器可以为饮料添加新的配料,同时保持饮料类的结构不变。
代理模式
代理模式为其他对象提供一种代理以控制对这个对象的访问。
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)
在这个例子中,ErrorLogger
、FileLogger
和 ConsoleLogger
形成了一条责任链。当调用 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)
在这个例子中,LightOnCommand
和 LightOffCommand
封装了对 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
接口的不同实现类表示,如 NoQuarterState
、HasQuarterState
等。根据机器的不同状态,对用户操作的响应也不同,并且状态之间可以相互转换。
组合模式
组合模式将对象组合成树形结构以表示“部分 - 整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
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 项目开发中,根据具体需求合理选择和应用设计模式,可以提高代码的可维护性、可扩展性和复用性。