Kotlin 移动开发中的设计模式:提升代码可维护性
关键词:Kotlin、设计模式、移动开发、可维护性、代码重构、MVVM、模式实践
摘要:在移动应用复杂度爆炸式增长的今天,代码可维护性已成为团队协作和长期迭代的核心挑战。Kotlin 作为 Android 官方推荐语言,凭借其简洁语法和现代特性(如扩展函数、协程、数据类),能让经典设计模式焕发新活力。本文将通过“天气应用从混乱到优雅”的真实故事,用“给小学生讲故事”的方式,拆解 Kotlin 中最常用的 5 类设计模式(创建型、结构型、行为型),并结合 Android 实战案例,教你如何用设计模式让代码更清晰、更易维护。
背景介绍
目的和范围
本文聚焦 Kotlin 移动开发场景,重点讲解如何通过设计模式解决实际开发中的痛点(如代码冗余、模块耦合、需求变更时的“牵一发而动全身”)。我们不会空谈 23 种经典模式,而是精选 最常用、最适配 Kotlin 特性的 7 种模式(单例、工厂、观察者、策略、装饰器、适配器、MVVM 变体),并给出 Android 工程中的真实代码示例。
预期读者
- 有基础的 Android 开发者(至少写过简单的 Activity 和 Fragment)
- 对 Kotlin 语法有初步了解(知道
object
、data class
、lambda
用法) - 遇到过“代码越写越乱”“改一个功能要改 10 个文件”等问题的同学
文档结构概述
本文将按“故事引入→核心模式讲解→Kotlin 特性优化→实战案例→避坑指南”的逻辑展开。你将看到:
- 一个“天气应用”从混乱到优雅的重构过程
- Kotlin 如何简化经典模式的实现(比如用
object
替代 Java 单例) - 结合 Android 架构组件(如 ViewModel、Flow)的模式实战
- 模式滥用的常见陷阱及规避方法
术语表
- 设计模式:针对常见软件设计问题的“通用解决方案模板”(比如“如何安全地创建唯一实例”)
- 可维护性:代码易于理解、修改、扩展的能力(比如新增功能时只需添加代码,而不是修改已有代码)
- Kotlin 特性:本文涉及的关键特性包括
object
(单例)、扩展函数
(轻量级装饰器)、lambda
(简化回调)、Flow
(响应式观察者)
核心概念与联系
故事引入:一个天气应用的“崩溃”与“重生”
小明是一名 Android 开发者,最近在做一个“智能天气助手”。初期需求简单:获取定位→请求天气 API→显示温度/湿度。他用 Kotlin 写了第一版代码:
// 初期代码(伪代码)
class WeatherActivity : AppCompatActivity() {
private val apiService = Retrofit.Builder().build().create(WeatherApi::class.java)
private val locationClient = LocationClient(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_weather)
// 1. 获取定位(直接调用系统 API)
locationClient.getLocation { lat, lng ->
// 2. 调用天气 API(直接使用 Retrofit)
apiService.getWeather(lat, lng).enqueue(object : Callback<Weather> {
override fun onResponse(call: Call<Weather>, response: Response<Weather>) {
// 3. 直接更新 UI(TextView、ImageView 一把梭)
tvTemp.text = response.body()?.temp
ivIcon.setImageResource(getIconId(response.body()?.icon))
}
})
}
}
}
初期运行良好,但随着需求增加,问题来了:
- 需求 1:新增“空气质量”数据 → 要改
getWeather
接口、UI 布局、onResponse 逻辑 - 需求 2:支持“手动切换城市” → 要复制定位逻辑,或者在
getLocation
里加判断 - 需求 3:优化性能(减少重复 API 请求) → 不知道哪里加缓存,改一处可能影响其他功能
小明意识到:代码像一团乱麻,每个需求都要“拆东墙补西墙”。这时候,设计模式登场了!
核心概念解释(像给小学生讲故事)
我们把设计模式比作“装修时的通用户型图”:不同户型(开发场景)对应不同的“户型图”(模式),能帮你避免“厨房和厕所门对门”(模块耦合)、“插座不够用”(扩展困难)等问题。
核心概念一:单例模式(创建型)
生活比喻:小区里的快递柜只有 1 个,所有住户(模块)都去这里取快递(共享实例),避免重复买柜子(重复创建对象)。
Kotlin 中的实现:Java 用“双重检查锁”写单例,Kotlin 直接用 object
关键字:
// 快递柜单例(天气 API 客户端)
object WeatherApiClient {
private val retrofit = Retrofit.Builder()
.baseUrl("https://api.weather.com/")
.build()
val service: WeatherApi by lazy { retrofit.create(WeatherApi::class.java) }
}
好处:全局唯一实例,避免重复创建 Retrofit 客户端(很耗资源!),所有模块用 WeatherApiClient.service
就能访问。
核心概念二:工厂模式(创建型)
生活比喻:奶茶店的“点单屏”——你选“全糖冰奶茶”(参数),机器自动做好(创建对象),你不用知道茶叶怎么泡、冰块怎么加(隐藏创建细节)。
Kotlin 中的实现:用 interface + 工厂函数
替代复杂的构造逻辑:
// 天气数据解析接口(可能有不同来源:API/本地缓存/模拟数据)
interface WeatherParser {
fun parse(rawData: String): Weather
}
// 工厂类(根据来源返回不同解析器)
object WeatherParserFactory {
fun create(type: DataSource): WeatherParser {
return when (type) {
DataSource.API -> ApiWeatherParser()
DataSource.CACHE -> CacheWeatherParser()
DataSource.MOCK -> MockWeatherParser()
}
}
}
好处:新增数据来源(比如“第三方 API”)时,只需加一个 ThirdPartyParser
并修改工厂,原有调用代码(如 WeatherParserFactory.create(DataSource.API)
)不用改。
核心概念三:观察者模式(行为型)
生活比喻:小区的“快递通知群”——你下单(事件)后,群里自动通知你(观察者)快递到了(状态变化),你不用每隔 5 分钟去快递柜看(轮询)。
Kotlin 中的实现:用 Android 的 LiveData
或 Kotlin 的 Flow
实现响应式更新:
// ViewModel 中的天气数据(被观察者)
class WeatherViewModel : ViewModel() {
private val _weather = MutableStateFlow<Weather?>(null)
val weather: StateFlow<Weather?> = _weather.asStateFlow()
fun loadWeather(lat: Double, lng: Double) {
viewModelScope.launch {
val rawData = WeatherApiClient.service.getWeather(lat, lng)
val parsed = WeatherParserFactory.create(DataSource.API).parse(rawData)
_weather.value = parsed // 数据变化时,自动通知观察者
}
}
}
// Activity 中观察数据(观察者)
class WeatherActivity : AppCompatActivity() {
private val viewModel: WeatherViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 观察 weather 变化,自动更新 UI
lifecycleScope.launch {
viewModel.weather.collect { weather ->
weather?.let { updateUI(it) }
}
}
}
}
好处:UI 和数据逻辑彻底解耦——数据变化时,UI 自动更新,无需手动调用 setText()
。
核心概念四:策略模式(行为型)
生活比喻:手机的“省电模式”“性能模式”——根据不同场景(策略),调整手机行为(算法),但切换模式时不用改手机硬件(代码结构)。
Kotlin 中的实现:用 interface + lambda
灵活替换算法:
// 通知策略接口(不同场景显示不同通知)
interface NotificationStrategy {
fun showNotification(context: Context, weather: Weather)
}
// 具体策略(暴雨预警)
class RainWarningStrategy : NotificationStrategy {
override fun showNotification(context: Context, weather: Weather) {
NotificationCompat.Builder(context, "rain_channel")
.setContentTitle("暴雨预警")
.setContentText("当前 ${weather.city} 有暴雨,注意安全!")
.show()
}
}
// 主类(通过构造注入策略)
class WeatherNotifier(private val strategy: NotificationStrategy) {
fun notify(context: Context, weather: Weather) {
strategy.showNotification(context, weather)
}
}
// 使用(动态切换策略)
val notifier = WeatherNotifier(
if (weather.isRain) RainWarningStrategy()
else NormalWeatherStrategy()
)
notifier.notify(context, weather)
好处:新增通知类型(如“高温预警”)时,只需加一个 HighTempStrategy
,无需修改 WeatherNotifier
类。
核心概念五:装饰器模式(结构型)
生活比喻:给手机套手机壳——手机(原有对象)的核心功能(打电话)不变,但新增了防摔(扩展功能)、卡通图案(附加功能)。
Kotlin 中的实现:用 扩展函数
替代传统装饰器类(更简洁!):
// 原始接口(天气数据)
interface WeatherData {
val temp: Int
val humidity: Int
}
// 扩展函数(给 WeatherData 增加“温度描述”功能)
fun WeatherData.getTempDescription(): String {
return when {
temp > 30 -> "炎热($temp℃)"
temp > 20 -> "温暖($temp℃)"
else -> "凉爽($temp℃)"
}
}
// 使用
val weather: WeatherData = ... // 某个实现类
val desc = weather.getTempDescription() // 直接调用扩展功能
好处:无需创建 WeatherDataDecorator
类,用 Kotlin 扩展函数“无侵入”地为现有类添加功能。
核心概念之间的关系(用小学生能理解的比喻)
设计模式不是“孤立的工具”,而是“团队协作的成员”。比如:
- 单例(快递柜) 和 工厂(奶茶机):工厂可能是一个单例(整个小区只有 1 台奶茶机),避免重复创建。
- 观察者(快递群) 和 策略(通知方式):快递到了(观察者触发事件),根据不同策略(普通通知/紧急通知)显示不同内容。
- 装饰器(手机壳) 和 工厂(手机店):工厂生产手机(原始对象),装饰器给手机加壳(扩展功能),用户拿到的是“带壳的手机”。
核心概念原理和架构的文本示意图
[创建型模式] → 解决“如何安全、灵活地创建对象”(单例、工厂)
[结构型模式] → 解决“如何组合对象/类,形成更灵活的结构”(装饰器、适配器)
[行为型模式] → 解决“对象之间如何通信、协作”(观察者、策略)
Kotlin 特性(object、扩展函数、Flow) → 简化模式实现 → 提升可维护性
Mermaid 流程图(模式协作示例)
graph TD
A[Activity] --> B[ViewModel] // 观察者模式(Activity观察ViewModel数据)
B --> C[WeatherApiClient] // 单例模式(全局唯一API客户端)
C --> D[WeatherParserFactory] // 工厂模式(创建解析器)
D --> E[ApiWeatherParser] // 具体解析策略
B --> F[WeatherNotifier] // 策略模式(动态选择通知方式)
F --> G[RainWarningStrategy] // 具体通知策略
核心算法原理 & 具体操作步骤
设计模式的“算法”更多是“结构设计”,我们以 观察者模式(Kotlin Flow 实现) 为例,拆解具体步骤:
步骤 1:定义被观察的数据流(StateFlow)
// ViewModel 中创建 MutableStateFlow(可修改的数据流)
private val _weather = MutableStateFlow<Weather?>(null)
// 暴露不可修改的 StateFlow(防止外部直接修改)
val weather: StateFlow<Weather?> = _weather.asStateFlow()
步骤 2:更新数据流(触发观察)
// 在协程中获取数据并更新
viewModelScope.launch {
val rawData = WeatherApiClient.service.getWeather(lat, lng) // 单例获取API客户端
val parsed = WeatherParserFactory.create(DataSource.API).parse(rawData) // 工厂创建解析器
_weather.value = parsed // 数据变化,自动通知观察者
}
步骤 3:观察数据流(UI 更新)
// Activity 中用 lifecycleScope 收集数据
lifecycleScope.launch {
// 观察 weather 数据流的变化
viewModel.weather.collect { weather ->
weather?.let {
tvTemp.text = it.getTempDescription() // 装饰器(扩展函数)提供的功能
showNotification(it) // 策略模式选择通知方式
}
}
}
数学模型和公式
设计模式的数学模型更多是“结构关系”,而非严格公式。以观察者模式为例,可抽象为:
被观察者
×
观察者集合
→
事件通知
\text{被观察者} \times \text{观察者集合} \rightarrow \text{事件通知}
被观察者×观察者集合→事件通知
其中:
- 被观察者(如
StateFlow
)维护一个观察者列表(collectors
) - 当被观察者状态变化(
_weather.value = newWeather
),遍历观察者列表并触发回调(collect
函数)
项目实战:天气应用重构完整案例
开发环境搭建
- Android Studio Flamingo | 2022.2.1(或更高)
- Kotlin 1.9.0+
- AndroidX 库(ViewModel 2.6.0+、Lifecycle 2.6.0+)
- Retrofit 2.9.0(网络请求)
- Kotlin Coroutines 1.7.0+(异步处理)
源代码详细实现和代码解读
1. 单例模式:网络客户端
// 单例实现(全局唯一的 Retrofit 客户端)
object WeatherApiClient {
// 使用 lazy 延迟初始化(第一次使用时创建)
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl("https://api.openweathermap.org/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
// 暴露 API 服务接口
val service: WeatherApi by lazy { retrofit.create(WeatherApi::class.java) }
}
// API 接口定义(Retrofit 注解)
interface WeatherApi {
@GET("data/2.5/weather")
suspend fun getWeather(
@Query("lat") lat: Double,
@Query("lon") lng: Double,
@Query("appid") appId: String = "YOUR_API_KEY"
): WeatherResponse
}
解读:object
保证全局唯一实例,lazy
避免启动时就创建(节省资源),suspend
配合协程实现异步请求。
2. 工厂模式:数据解析器
// 解析器接口(抽象产品)
interface WeatherParser {
fun parse(response: WeatherResponse): Weather
}
// 具体解析器(真实数据)
class RealWeatherParser : WeatherParser {
override fun parse(response: WeatherResponse): Weather {
return Weather(
city = response.name,
temp = response.main.temp - 273.15, // 开尔文转摄氏度
humidity = response.main.humidity,
icon = response.weather.firstOrNull()?.icon
)
}
}
// 具体解析器(模拟数据,用于测试)
class MockWeatherParser : WeatherParser {
override fun parse(response: WeatherResponse): Weather {
return Weather(
city = "模拟城市",
temp = 25.0,
humidity = 60,
icon = "01d"
)
}
}
// 工厂类(创建具体解析器)
object WeatherParserFactory {
fun create(type: DataSource): WeatherParser {
return when (type) {
DataSource.REAL -> RealWeatherParser()
DataSource.MOCK -> MockWeatherParser()
}
}
}
// 数据源枚举
enum class DataSource { REAL, MOCK }
解读:通过 when
分支返回不同解析器,新增数据源(如 CACHE
)时只需添加新的解析器类和工厂分支,符合“开闭原则”。
3. 观察者模式:数据与 UI 解耦
// ViewModel(被观察者)
class WeatherViewModel : ViewModel() {
// 可修改的数据流(初始值为 null)
private val _weather = MutableStateFlow<Weather?>(null)
// 暴露不可修改的数据流
val weather: StateFlow<Weather?> = _weather.asStateFlow()
// 加载天气数据(协程中执行)
fun loadWeather(lat: Double, lng: Double, source: DataSource = DataSource.REAL) {
viewModelScope.launch {
try {
// 使用单例获取 API 客户端,工厂获取解析器
val response = WeatherApiClient.service.getWeather(lat, lng)
val parser = WeatherParserFactory.create(source)
val parsedWeather = parser.parse(response)
_weather.value = parsedWeather // 更新数据,触发观察
} catch (e: Exception) {
_weather.value = null // 错误时置空,UI 显示错误提示
}
}
}
}
// Activity(观察者)
class WeatherActivity : AppCompatActivity() {
// 通过 ViewModelProvider 获取 ViewModel(自动关联生命周期)
private val viewModel: WeatherViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_weather)
// 观察天气数据变化(Lifecycle 感知,避免内存泄漏)
lifecycleScope.launch {
viewModel.weather.collect { weather ->
weather?.let { updateUI(it) } ?: showError()
}
}
// 模拟定位按钮点击(触发加载数据)
btnRefresh.setOnClickListener {
// 假设获取到经纬度(实际需调用定位 SDK)
viewModel.loadWeather(30.0, 120.0)
}
}
private fun updateUI(weather: Weather) {
tvCity.text = weather.city
tvTemp.text = "温度:${weather.temp.format(1)}℃" // 扩展函数(装饰器)
tvHumidity.text = "湿度:${weather.humidity}%"
ivWeatherIcon.setImageResource(getIconRes(weather.icon))
}
private fun showError() {
Toast.makeText(this, "获取天气失败", Toast.LENGTH_SHORT).show()
}
}
// 扩展函数(装饰器模式:为 Double 添加格式化功能)
fun Double.format(decimal: Int): String {
return "%.${decimal}f".format(this)
}
解读:通过 StateFlow
实现响应式数据更新,ViewModel
负责业务逻辑,Activity
只处理 UI,彻底解耦。扩展函数 format
无侵入地为 Double
添加格式化功能。
4. 策略模式:智能通知
// 通知策略接口
interface NotificationStrategy {
fun showNotification(context: Context, weather: Weather)
}
// 暴雨预警策略
class RainWarningStrategy : NotificationStrategy {
override fun showNotification(context: Context, weather: Weather) {
if (weather.icon?.startsWith("09") == true || weather.icon == "10d") { // 暴雨/大雨图标
createNotificationChannel(context, "rain_channel", "暴雨预警")
NotificationCompat.Builder(context, "rain_channel")
.setSmallIcon(R.drawable.ic_rain_warning)
.setContentTitle("暴雨预警")
.setContentText("${weather.city} 目前有暴雨,出行请注意安全!")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.show()
}
}
}
// 高温预警策略
class HeatWarningStrategy : NotificationStrategy {
override fun showNotification(context: Context, weather: Weather) {
if (weather.temp > 35.0) {
createNotificationChannel(context, "heat_channel", "高温预警")
NotificationCompat.Builder(context, "heat_channel")
.setSmallIcon(R.drawable.ic_heat_warning)
.setContentTitle("高温预警")
.setContentText("${weather.city} 目前气温 ${weather.temp.format(1)}℃,请注意防暑!")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.show()
}
}
}
// 通知管理器(依赖策略接口)
class WeatherNotificationManager(private val strategies: List<NotificationStrategy>) {
fun checkAndNotify(context: Context, weather: Weather) {
strategies.forEach { it.showNotification(context, weather) }
}
}
// 使用(在 ViewModel 中触发通知)
class WeatherViewModel : ViewModel() {
// ...(其他代码)
private val notificationManager = WeatherNotificationManager(
listOf(RainWarningStrategy(), HeatWarningStrategy())
)
private fun notifyWeather(context: Context, weather: Weather) {
notificationManager.checkAndNotify(context, weather)
}
}
解读:通过策略模式,新增“台风预警”只需添加 TyphoonWarningStrategy
并加入 notificationManager
的列表,无需修改现有代码。
实际应用场景
模式 | 典型场景 | Kotlin 优势 |
---|---|---|
单例模式 | 网络客户端(Retrofit)、数据库管理器(Room Database) | object 关键字一行实现,无需双重检查锁 |
工厂模式 | 不同环境(开发/测试/生产)的配置对象、多数据源(API/缓存/模拟)的解析器 | when 分支清晰,结合 interface 隐藏创建细节 |
观察者模式 | UI 数据更新(ViewModel → Activity/Fragment)、事件总线(如组件间通信) | Flow /LiveData 原生支持生命周期感知,代码简洁 |
策略模式 | 不同业务规则(如会员/非会员折扣)、多通知类型(普通/紧急/静默) | lambda 或 interface 实现灵活替换,避免 if-else 地狱 |
装饰器模式 | 为现有类添加扩展功能(如数据格式化、日志记录) | 扩展函数“无侵入”添加功能,无需创建装饰器类 |
工具和资源推荐
- 官方文档:
- Kotlin 官方设计模式指南(含单例、工厂等模式的 Kotlin 实现)
- Android 架构指南(结合 MVVM、观察者模式的最佳实践)
- 书籍:
- 《Head First 设计模式(中文版)》(用故事讲解模式,适合入门)
- 《Kotlin 核心编程》(第 10 章专门讲解 Kotlin 对设计模式的优化)
- 工具库:
kotlinx-coroutines-core
(协程,简化异步观察者模式)androidx.lifecycle-viewmodel-ktx
(ViewModel,配合观察者模式管理数据)kotlinx-datetime
(扩展函数,装饰器模式的典型应用)
未来发展趋势与挑战
趋势 1:Kotlin 多平台(KMP)对模式的影响
Kotlin 多平台(KMP)允许共享业务逻辑(如工厂、策略模式)在 Android、iOS、后端之间,未来设计模式将更注重“跨平台通用性”。例如,用同一套工厂模式代码创建不同平台的数据库客户端。
趋势 2:函数式编程与模式融合
Kotlin 支持函数式编程(如高阶函数、lambda),未来可能出现“函数式设计模式”。例如,用 高阶函数
替代传统的策略模式(无需定义接口,直接传 lambda):
// 函数式策略模式(无需接口)
fun showNotification(context: Context, weather: Weather, strategy: (Context, Weather) -> Unit) {
strategy(context, weather)
}
// 使用
showNotification(context, weather) { ctx, w ->
// 具体通知逻辑(lambda 替代策略类)
}
挑战:模式滥用导致“过度设计”
新手容易“为了模式而模式”,比如用工厂模式创建简单对象(直接 new
更高效)、用观察者模式处理简单的 UI 更新(直接调用 setText
更直观)。原则:模式是“解决方案”,不是“必须遵守的规则”——当代码出现重复、耦合时再用模式,否则保持简单!
总结:学到了什么?
核心概念回顾
- 单例模式:用
object
轻松实现全局唯一实例(如网络客户端)。 - 工厂模式:通过
interface + 工厂类
隐藏对象创建细节(如多数据源解析器)。 - 观察者模式:用
Flow
/LiveData
实现响应式数据更新(UI 与逻辑解耦)。 - 策略模式:通过
interface
或lambda
灵活替换算法(如多通知类型)。 - 装饰器模式:用扩展函数“无侵入”添加功能(如数据格式化)。
概念关系回顾
设计模式是“协作的团队”:
- 单例提供全局资源 → 工厂使用单例创建对象 → 观察者监听工厂创建的数据 → 策略处理数据的不同行为 → 装饰器扩展数据的功能。
思考题:动动小脑筋
-
场景题:你的项目中有一个
ImageLoader
类,负责加载网络图片。现在需要支持“普通加载”(无缓存)、“缓存加载”(先读缓存,没有再下载)、“测试加载”(总是返回本地图片)三种模式。你会用哪种设计模式?如何用 Kotlin 实现? -
优化题:你在代码中看到这样的逻辑:
if (user.type == "VIP") { applyVipDiscount() } else if (user.type == "NORMAL") { applyNormalDiscount() } else if (user.type == "GUEST") { applyGuestDiscount() }
这里存在什么问题?如何用设计模式优化?(提示:策略模式)
-
发散题:Kotlin 的
扩展函数
可以替代传统装饰器模式,那它有什么局限性?(提示:无法访问私有成员、无法继承)
附录:常见问题与解答
Q1:设计模式会不会让代码变复杂?
A:初期可能增加类的数量,但长期看能降低维护成本。比如工厂模式初期要写接口和工厂类,但新增功能时只需添加代码,而不是修改已有代码(符合“开闭原则”)。
Q2:Kotlin 的 object
单例是线程安全的吗?
A:是的!Kotlin 的 object
编译后是 Java 的 static final
类,实例在类加载时创建(JVM 保证线程安全)。如果需要延迟初始化(如依赖外部参数),可以用 lazy
委托:
object WeatherApiClient {
val service: WeatherApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Retrofit.Builder().build().create(WeatherApi::class.java)
}
}
Q3:什么时候用观察者模式?什么时候直接调用?
A:当“数据变化需要通知多个未知的观察者”时用观察者模式(如 UI 组件、跨模块通信);当“数据变化只影响一个明确的对象”时直接调用(如 Activity
调用 TextView.setText()
)。
扩展阅读 & 参考资料
- 《设计模式:可复用面向对象软件的基础》(Erich Gamma 等,经典 GoF 模式原著)
- Kotlin 官方设计模式文档
- Android Developers:架构组件指南
- 《Kotlin in Action》(Dmitry Jemerov 等,第 10 章“函数式编程”)