《Kotlin核心编程》下篇

设计模式

设计模式分类

创建型设计模式:

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

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

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

结构型设计模式:

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

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

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

行为型设计模式:

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

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

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

工厂模式

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

Kotlin 复制代码
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
        }
    }
}

使用示例:

Kotlin 复制代码
fun main() {
    val factory = ShapeFactory()
    val circle = factory.createShape("CIRCLE")
    circle?.draw()
}

内联函数简化抽象工厂

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

Kotlin 复制代码
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")
}

使用示例:

Kotlin 复制代码
fun main() {
    val connection = createDatabaseConnection("MYSQL")
    connection.connect()
}

构建者模式

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

Kotlin 复制代码
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)
    }
}

使用示例:

Kotlin 复制代码
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

Kotlin 复制代码
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")
    }
}

使用示例:

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

高阶函数简化设计模式

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

Kotlin 复制代码
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方法可以使自定义类支持迭代操作。例如,创建一个简单的自定义集合类:
Kotlin 复制代码
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)
    }
}
  • 责任链模式:责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。例如,处理请假审批流程:
Kotlin 复制代码
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)可以很好地实现状态模式。状态模式允许对象在内部状态改变时改变它的行为。例如,实现一个简单的电梯状态管理:
Kotlin 复制代码
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,可以通过装饰类为咖啡添加不同配料:

Kotlin 复制代码
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 中,函数可以像普通数据类型一样进行操作,例如作为参数传递给其他函数、作为函数的返回值,或者存储在变量中。
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 中,使用集合操作函数体现声明式编程风格,只需要声明对数据的操作,而不需要关心具体的实现细节。
Kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)
// 筛选出偶数并计算平方
val squaredEvenNumbers = numbers.filter { it % 2 == 0 }.map { it * it }
println(squaredEvenNumbers)

引用透明性和副作用

  • 引用透明性:满足引用透明性的函数,给定相同的输入,总是返回相同的输出,并且不会产生任何可观察的副作用。
Kotlin 复制代码
// 一个具有引用透明性的纯函数
fun square(x: Int): Int = x * x
// 无论何时调用,结果都相同
val result1 = square(5)
val result2 = square(5)
println(result1)
println(result2)
  • 副作用:副作用是指函数在执行过程中,除了返回值之外,对外部状态产生的影响,如修改全局变量、进行 I/O 操作等。
Kotlin 复制代码
// 定义一个全局变量
var globalVar = 0
// 一个具有副作用的函数,修改全局变量
fun incrementGlobal() {
    globalVar++
}
// 调用具有副作用的函数
incrementGlobal()
println(globalVar)

纯函数和局部可变性

  • 纯函数:纯函数是满足引用透明性的函数,不依赖外部状态,只根据输入参数返回确定的输出。
Kotlin 复制代码
// 纯函数示例,计算两个数的商
fun divide(a: Int, b: Int): Double {
    require(b!= 0) { "除数不能为零" }
    return a.toDouble() / b
}
// 调用纯函数
val divisionResult = divide(10, 2)
println(divisionResult)
  • 局部可变性:虽然函数式编程强调不可变性,但在某些情况下,在局部范围内使用可变变量可以提高效率或实现特定算法。
Kotlin 复制代码
// 在函数内部使用可变变量实现累加
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 类:
Kotlin 复制代码
class MyThread : Thread() {
    override fun run() {
        // 线程执行逻辑
    }
}
val myThread = MyThread()
myThread.start()
  • 线程安全:多线程访问共享资源时,可能引发数据不一致问题。解决方法有:

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

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

Kotlin 复制代码
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。

Kotlin 复制代码
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}"
            }
        }
    }
}
相关推荐
tangweiguo030519875 小时前
Android全局网络监控最佳实践(Kotlin实现)
android·kotlin
移动开发者1号6 小时前
Android后台服务保活方案对比分析
android·kotlin
移动开发者1号6 小时前
ContentProvider URI匹配机制详解
android·kotlin
zhifanxu18 小时前
android协程异步编程常用方法
android·开发语言·kotlin
Mr YiRan1 天前
Kotlin委托机制使用方式和原理
android·开发语言·kotlin
移动开发者1号1 天前
扫一扫的时候会经历哪些事
android·kotlin
移动开发者1号1 天前
动态加载组件原理详解
android·kotlin
移动开发者1号1 天前
Android任务栈管理策略总结
android·kotlin
墨狂之逸才1 天前
kotlin泛型实化
android·kotlin
墨狂之逸才1 天前
kotlin中:: 是一个非常重要的操作符,称为引用操作符
android·kotlin