Bootstrap

《Kotlin核心编程》下篇

设计模式

设计模式分类

创建型设计模式:

  • 概念:创建型设计模式主要用于对象的创建过程,比如对象的实例化、创建对象的方式和时机等,它关注的是如何将对象的创建和使用分离,使得代码在创建对象时更加灵活、可维护和可扩展。

  • 特点:封装对象创建过程;提高灵活性;便于代码复用。

  • 常见模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。

结构型设计模式:

  • 概念:结构型设计模式主要用于处理类或对象的组合结构,它关注的是如何将类或对象组合成更大的结构,以及如何在这些结构中实现功能的扩展和优化。

  • 特点:组合对象结构;增强功能;提高可维护性。

  • 常见模式:代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式。

行为型设计模式:

  • 概念:行为型设计模式主要用于处理对象之间的交互和职责分配,它关注的是对象之间如何进行通信、协作以及如何分配职责,以实现系统的功能和行为。

  • 特点:对象间交互;职责分配;提高可扩展性。

  • 常见模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

工厂模式

工厂模式是一种创建对象的设计模式,它将对象的创建和使用分离。通过使用工厂类来创建对象,而不是在客户端代码中直接实例化,这样可以降低代码的耦合度,提高可维护性和可扩展性。例如,有一个Shape接口及其实现类CircleRectangle,可以创建一个ShapeFactory工厂类:

interface Shape {
    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) {
            "CIRCLE" -> Circle()
            "RECTANGLE" -> Rectangle()
            else -> null
        }
    }
}

使用示例:

fun main() {
    val factory = ShapeFactory()
    val circle = factory.createShape("CIRCLE")
    circle?.draw()
}

内联函数简化抽象工厂

内联函数在编译时会将函数体直接插入到调用处,减少函数调用开销。在抽象工厂模式中,使用内联函数可以简化代码结构。例如,定义一个内联的抽象工厂函数,用于创建不同类型的数据库连接:

interface DatabaseConnection {
    fun connect()
}

class MySQLConnection : DatabaseConnection {
    override fun connect() {
        println("Connecting to MySQL database")
    }
}

class PostgreSQLConnection : DatabaseConnection {
    override fun connect() {
        println("Connecting to PostgreSQL database")
    }
}

inline fun createDatabaseConnection(type: String): DatabaseConnection = when (type) {
    "MYSQL" -> MySQLConnection()
    "POSTGRESQL" -> PostgreSQLConnection()
    else -> throw IllegalArgumentException("Unsupported database type")
}

使用示例:

fun main() {
    val connection = createDatabaseConnection("MYSQL")
    connection.connect()
}

构建者模式

构建者模式用于创建复杂对象,将对象的构建过程和表示分离,使得同样的构建过程可以创建不同的表示。比如创建一个Computer类,其配置较为复杂:

class Computer(
    val cpu: String,
    val ram: String,
    val storage: String
)

class ComputerBuilder {
    private var cpu: String = ""
    private var ram: String = ""
    private var storage: String = ""

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

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

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

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

使用示例:

fun main() {
    val computer = ComputerBuilder()
      .setCpu("Intel i7")
      .setRam("16GB")
      .setStorage("1TB SSD")
      .build()
    println("Computer configured: CPU - ${computer.cpu}, RAM - ${computer.ram}, Storage - ${computer.storage}")
}

观察者模式

观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新 。在 Kotlin 中可以通过委托来实现观察者模式。假设有一个被观察的Subject类和多个Observer

interface Observer {
    fun update(message: String)
}

class Subject {
    private val observers: MutableList<Observer> = mutableListOf()

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

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

    fun notifyObservers(message: String) {
        observers.forEach { it.update(message) }
    }
}

class ConcreteObserver : Observer {
    override fun update(message: String) {
        println("Observer received message: $message")
    }
}

使用示例:

fun main() {
    val subject = Subject()
    val observer = ConcreteObserver()
    subject.registerObserver(observer)
    subject.notifyObservers("Something has changed!")
}

高阶函数简化设计模式

高阶函数可以以函数作为参数或返回值,利用这一特性可以简化一些设计模式的实现。例如策略模式,它定义了一系列算法,将每个算法都封装起来,并且使它们可以相互替换。通过高阶函数实现一个简单的计算策略模式:

typealias MathOperation = (Int, Int) -> Int

fun calculate(a: Int, b: Int, operation: MathOperation): Int {
    return operation(a, b)
}

fun main() {
    val add: MathOperation = { x, y -> x + y }
    val subtract: MathOperation = { x, y -> x - y }

    val result1 = calculate(5, 3, add)
    val result2 = calculate(5, 3, subtract)

    println("Addition result: $result1")
    println("Subtraction result: $result2")
}

重载 iterator 方法、责任链模式、ADT 实现状态模式

  • 重载 iterator 方法:在 Kotlin 中,通过重载iterator方法可以使自定义类支持迭代操作。例如,创建一个简单的自定义集合类:

class MyList<T>(private val elements: Array<T>) : Iterable<T> {
    override fun iterator(): Iterator<T> = object : Iterator<T> {
        private var index = 0
        override fun hasNext(): Boolean = index < elements.size
        override fun next(): T = elements[index++]
    }
}

fun main() {
    val myList = MyList(arrayOf(1, 2, 3))
    for (element in myList) {
        println(element)
    }
}
  • 责任链模式:责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。例如,处理请假审批流程:

abstract class Approver {
    protected var successor: Approver? = null

    fun setSuccessor(successor: Approver) {
        this.successor = successor
    }

    abstract fun processRequest(request: Int)
}

class TeamLead : Approver() {
    override fun processRequest(request: Int) {
        if (request <= 2) {
            println("Team lead approved the leave for $request days")
        } else {
            successor?.processRequest(request)
        }
    }
}

class Manager : Approver() {
    override fun processRequest(request: Int) {
        if (request <= 5) {
            println("Manager approved the leave for $request days")
        } else {
            successor?.processRequest(request)
        }
    }
}

class Director : Approver() {
    override fun processRequest(request: Int) {
        if (request <= 10) {
            println("Director approved the leave for $request days")
        } else {
            println("Request needs further discussion")
        }
    }
}

//使用示例
fun main() {
    val teamLead = TeamLead()
    val manager = Manager()
    val director = Director()

    teamLead.setSuccessor(manager)
    manager.setSuccessor(director)

    teamLead.processRequest(3)
}
  • ADT 实现状态模式:代数数据类型(ADT)可以很好地实现状态模式。状态模式允许对象在内部状态改变时改变它的行为。例如,实现一个简单的电梯状态管理:

sealed class ElevatorState
class Idle : ElevatorState()
class MovingUp : ElevatorState()
class MovingDown : ElevatorState()

class Elevator {
    private var state: ElevatorState = Idle()

    fun moveUp() {
        when (state) {
            is Idle -> {
                state = MovingUp()
                println("Elevator is moving up")
            }
            is MovingDown -> {
                state = MovingUp()
                println("Elevator stopped and is moving up")
            }
            else -> println("Elevator is already moving up")
        }
    }

    fun moveDown() {
        when (state) {
            is Idle -> {
                state = MovingDown()
                println("Elevator is moving down")
            }
            is MovingUp -> {
                state = MovingDown()
                println("Elevator stopped and is moving down")
            }
            else -> println("Elevator is already moving down")
        }
    }
}

//使用示例
fun main() {
    val elevator = Elevator()
    elevator.moveUp()
    elevator.moveDown()}
}

装饰模式

装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式相比生成子类更为灵活。例如,有一个Coffee接口及其实现类SimpleCoffee,可以通过装饰类为咖啡添加不同配料:

interface Coffee {
    fun getCost(): Double
    fun getDescription(): String
}

class SimpleCoffee : Coffee {
    override fun getCost(): Double = 1.0
    override fun getDescription(): String = "Simple coffee"
}

class MilkDecorator(private val coffee: Coffee) : Coffee {
    override fun getCost(): Double = coffee.getCost() + 0.5
    override fun getDescription(): String = "${coffee.getDescription()}, with milk"
}

class SugarDecorator(private val coffee: Coffee) : Coffee {
    override fun getCost(): Double = coffee.getCost() + 0.2
    override fun getDescription(): String = "${coffee.getDescription()}, with sugar"
}

//使用示例
fun main() {
    val simpleCoffee = SimpleCoffee()
    val coffeeWithMilk = MilkDecorator(simpleCoffee)
    val coffeeWithMilkAndSugar = SugarDecorator(coffeeWithMilk)

    println("${coffeeWithMilkAndSugar.getDescription()}: ${coffeeWithMilkAndSugar.getCost()}")
}

函数式编程

函数式语言

  • 函数是一等公民:在 Kotlin 中,函数可以像普通数据类型一样进行操作,例如作为参数传递给其他函数、作为函数的返回值,或者存储在变量中。

//1.第一个示例:
//定义一个函数并赋值给变量
val add: (Int, Int) -> Int = { a, b -> a + b }
// 使用变量调用函数
val result = add(3, 5)
println(result)

//2.第二个示例:函数作为参数传递给其他函数
// 定义一个接受函数作为参数的高阶函数
fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}
// 定义一个简单的乘法函数
val multiply: (Int, Int) -> Int = { a, b -> a * b }
// 将乘法函数作为参数传递给高阶函数
val multiplyResult = operateOnNumbers(4, 6, multiply)
println(multiplyResult)

//3.第三个示例:函数作为返回值
// 定义一个根据条件返回不同函数的高阶函数
fun getOperation(shouldAdd: Boolean): (Int, Int) -> Int {
    return if (shouldAdd) {
        { a, b -> a + b }
    } else {
        { a, b -> a - b }
    }
}
// 根据条件获取函数并调用
val operation = getOperation(true)
val operationResult = operation(10, 5)
println(operationResult)
  • 声明式编程风格:在 Kotlin 中,使用集合操作函数体现声明式编程风格,只需要声明对数据的操作,而不需要关心具体的实现细节。

val numbers = listOf(1, 2, 3, 4, 5)
// 筛选出偶数并计算平方
val squaredEvenNumbers = numbers.filter { it % 2 == 0 }.map { it * it }
println(squaredEvenNumbers)

引用透明性和副作用

  • 引用透明性:满足引用透明性的函数,给定相同的输入,总是返回相同的输出,并且不会产生任何可观察的副作用。

// 一个具有引用透明性的纯函数
fun square(x: Int): Int = x * x
// 无论何时调用,结果都相同
val result1 = square(5)
val result2 = square(5)
println(result1)
println(result2)
  • 副作用:副作用是指函数在执行过程中,除了返回值之外,对外部状态产生的影响,如修改全局变量、进行 I/O 操作等。

// 定义一个全局变量
var globalVar = 0
// 一个具有副作用的函数,修改全局变量
fun incrementGlobal() {
    globalVar++
}
// 调用具有副作用的函数
incrementGlobal()
println(globalVar)

纯函数和局部可变性

  • 纯函数:纯函数是满足引用透明性的函数,不依赖外部状态,只根据输入参数返回确定的输出。

// 纯函数示例,计算两个数的商
fun divide(a: Int, b: Int): Double {
    require(b!= 0) { "除数不能为零" }
    return a.toDouble() / b
}
// 调用纯函数
val divisionResult = divide(10, 2)
println(divisionResult)
  • 局部可变性:虽然函数式编程强调不可变性,但在某些情况下,在局部范围内使用可变变量可以提高效率或实现特定算法。

// 在函数内部使用可变变量实现累加
fun sumList(list: List<Int>): Int {
    var result = 0
    list.forEach { result += it }
    return result
}
val numbersList = listOf(1, 2, 3, 4, 5)
val sum = sumList(numbersList)
println(sum)

异步和并发

基本概念

  • 异步:程序在执行过程中,某些操作无需等待当前任务完成,就能继续执行后续代码,不会阻塞主线程,提升用户体验。比如在 Android 应用中,进行网络请求时采用异步方式,主线程不会因等待数据而卡顿,用户可继续操作界面。在 Kotlin 中,异步主要通过协程来实现。协程是一种轻量级的线程模型,它基于挂起函数(suspend function)来暂停和恢复执行。挂起函数可以暂停当前协程的执行,并将控制权交回给调用者,而不会阻塞底层线程。这样,其他协程就可以在同一线程上运行,从而实现了异步执行。

  • 并发:指在同一时间段内,多个任务看似同时执行。在单核 CPU 系统里,通过任务快速切换模拟同时执行;多核 CPU 系统则能真正并行执行任务,充分利用系统资源,提高程序运行效率。

核心技术

  • 协程

    • 挂起函数:以 suspend 关键字修饰,可暂停执行并将控制权交回调用者,不阻塞线程。常用于异步操作,如网络请求、文件读取等。例如:suspend fun readFile() : String,函数内部执行文件读取时可暂停,等待数据读取完毕再恢复。

    • 协程构建器:

      • launch:启动新协程并立即执行,无返回值。主要用于执行独立的异步任务,如更新 UI、记录日志等。例如:launch { doSomeBackgroundWork() }

      • async:启动协程并返回 Deferred 对象,通过 await 方法获取协程执行结果。适用于需要异步计算并获取结果的场景,如多个数据的并行计算。例如:val deferredResult = async { calculateValue() }; val result = deferredResult.await()

    • 协程上下文与调度器:协程上下文包含协程运行的环境信息,调度器是其中关键部分,决定协程执行的线程或线程池。

      • Dispatchers.Main:用于 Android 主线程,更新 UI 等操作必须在此调度器执行。

      • Dispatchers.IO:适合 I/O 密集型任务,如文件读写、网络请求,它使用一个线程池来处理这些任务。

      • Dispatchers.Default:适用于 CPU 密集型任务,利用共享线程池执行,充分利用多核 CPU 性能。

  • 线程

    • 创建方式:可通过继承 Thread 类或实现 Runnable 接口创建线程。不过,Kotlin 中协程提供了更简洁、高效的异步处理方式,线程使用相对较少。例如继承 Thread 类:

class MyThread : Thread() {
    override fun run() {
        // 线程执行逻辑
    }
}
val myThread = MyThread()
myThread.start()
  • 线程安全:多线程访问共享资源时,可能引发数据不一致问题。解决方法有:

    • 同步块:使用 synchronized 关键字包裹共享资源访问代码,保证同一时刻只有一个线程能访问。

    • 锁机制:ReentrantLock 等锁类提供更灵活的同步控制。使用时先获取锁,操作完成后释放锁。

val lock = ReentrantLock()
lock.lock()
try {
    // 访问共享资源的代码
} finally {
    lock.unlock()
}

实际应用

  • 网络请求:结合协程与网络框架(如 OkHttp),实现异步网络请求。在 Dispatchers.IO 调度器执行请求,获取数据后切换到 Dispatchers.Main 更新 UI。例如:

    • 当按钮被点击时,使用 CoroutineScope(Dispatchers.Main).launch 在主线程启动一个协程。

    • 在协程中调用 fetchData 函数进行网络请求,fetchData 函数使用 withContext(Dispatchers.IO) 将网络请求操作切换到 Dispatchers.IO 调度器执行,避免阻塞主线程。

    • 当网络请求完成后,将结果赋值给 resultTextViewtext 属性,由于协程在主线程启动,所以可以直接更新 UI。

class MainActivity : AppCompatActivity() {
    private lateinit var fetchDataButton: Button
    private lateinit var resultTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fetchDataButton = findViewById(R.id.fetchDataButton)
        resultTextView = findViewById(R.id.resultTextView)

        fetchDataButton.setOnClickListener {
            // 在主线程启动协程
            CoroutineScope(Dispatchers.Main).launch {
                try {
                    // 调用 fetchData 函数进行网络请求
                    val result = fetchData()
                    // 在主线程更新 UI
                    resultTextView.text = result
                } catch (e: Exception) {
                    resultTextView.text = "Error: ${e.message}"
                }
            }
        }
    }

    private suspend fun fetchData(): String {
        return withContext(Dispatchers.IO) {
            val client = OkHttpClient()
            val request = Request.Builder()
               .url("https://jsonplaceholder.typicode.com/todos/1")
               .build()
            try {
                val response = client.newCall(request).execute()
                response.body?.string() ?: ""
            } catch (e: IOException) {
                "Network error: ${e.message}"
            }
        }
    }
}

;