设计模式
设计模式分类
创建型设计模式:
-
概念:创建型设计模式主要用于对象的创建过程,比如对象的实例化、创建对象的方式和时机等,它关注的是如何将对象的创建和使用分离,使得代码在创建对象时更加灵活、可维护和可扩展。
-
特点:封装对象创建过程;提高灵活性;便于代码复用。
-
常见模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
结构型设计模式:
-
概念:结构型设计模式主要用于处理类或对象的组合结构,它关注的是如何将类或对象组合成更大的结构,以及如何在这些结构中实现功能的扩展和优化。
-
特点:组合对象结构;增强功能;提高可维护性。
-
常见模式:代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式。
行为型设计模式:
-
概念:行为型设计模式主要用于处理对象之间的交互和职责分配,它关注的是对象之间如何进行通信、协作以及如何分配职责,以实现系统的功能和行为。
-
特点:对象间交互;职责分配;提高可扩展性。
-
常见模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
工厂模式
工厂模式是一种创建对象的设计模式,它将对象的创建和使用分离。通过使用工厂类来创建对象,而不是在客户端代码中直接实例化,这样可以降低代码的耦合度,提高可维护性和可扩展性。例如,有一个Shape
接口及其实现类Circle
和Rectangle
,可以创建一个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
调度器执行,避免阻塞主线程。 -
当网络请求完成后,将结果赋值给
resultTextView
的text
属性,由于协程在主线程启动,所以可以直接更新 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}"
}
}
}
}