Android 模块化、组件化与插件化实践

从基础概念到实战应用,系统梳理 Android 架构演进之路


一、基础概念

1.1 为什么需要架构优化?

随着业务迭代,单体 App 会遇到诸多问题:

问题 表现 影响
代码耦合严重 模块间直接 import,循环依赖 难以维护、编译慢
编译效率低 改一行代码全量编译 开发效率下降
团队协作冲突 多人修改同一工程 Git 冲突频繁
无法独立开发 强依赖主工程 无法并行开发、独立测试
复用困难 业务逻辑与 UI 混在一起 跨项目复用成本高

1.2 三种架构模式辨析

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────────────────────────────────┐
│ 架构演进路径 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 单体架构 ──► 模块化 ──► 组件化 ──► 插件化 │
│ │ │ │ │ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 按功能拆分 Gradle模块 路由解耦 运行时动态加载 │
│ 包结构划分 build独立 ARouter等 热更新/按需下载 │
│ │
└─────────────────────────────────────────────────────────────────┘

模块化(Modularization)

  • 定义:按业务功能将代码拆分为独立的 Gradle 模块,每个模块有清晰的 build.gradle 和边界
  • 特点:物理隔离、可独立编译,通常仍在同一 Git 仓库内
  • 粒度:中等,如「用户模块」「订单模块」「支付模块」

组件化(Componentization)

  • 定义:在模块化基础上,通过路由/服务发现实现模块间完全解耦,可独立开发、独立运行
  • 特点:依赖倒置、接口隔离、可单模块调试(Application 切换)
  • 粒度:较细,如「登录组件」「分享组件」「埋点组件」

插件化(Pluginization)

  • 定义:插件 APK/Dex 可动态加载、热更新,主 App 与插件解耦到运行时
  • 特点:运行时动态、按需下载、可热修复、减小包体积
  • 粒度:独立 APK/模块

二、模块化原理与实践

2.1 模块化的核心原则

  1. 单一职责:每个模块只负责一块业务
  2. 接口隔离:模块间通过接口/路由通信,不暴露实现细节
  3. 依赖倒置:依赖抽象(interface)而非具体实现

2.2 Gradle 多模块目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MyApp/
├── app/ # 主工程壳
│ └── build.gradle
├── module_user/ # 用户模块
│ ├── build.gradle
│ └── src/main/
├── module_order/ # 订单模块
│ └── build.gradle
├── module_payment/ # 支付模块
│ └── build.gradle
├── common_base/ # 公共基础库
│ ├── network/
│ ├── utils/
│ └── base/
├── common_router/ # 路由层
│ └── build.gradle
├── build.gradle
└── settings.gradle

2.3 settings.gradle 与模块声明

1
2
3
4
5
6
7
8
// settings.gradle
rootProject.name = "MyApp"
include ':app'
include ':module_user'
include ':module_order'
include ':module_payment'
include ':common_base'
include ':common_router'

2.4 模块间依赖配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// app/build.gradle - 主工程依赖各业务模块
dependencies {
implementation project(':module_user')
implementation project(':module_order')
implementation project(':module_payment')
implementation project(':common_router')
implementation project(':common_base')
}

// module_user/build.gradle - 业务模块只依赖基础库和路由
dependencies {
implementation project(':common_base')
implementation project(':common_router')
// 不要依赖 module_order、module_payment!
}

// common_base/build.gradle - 基础库不依赖任何业务模块
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
}

2.5 模块间通信:接口 + 实现注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// common_router 中定义接口
interface IUserService {
fun getCurrentUser(): User?
fun logout()
}

// module_user 中实现接口
class UserServiceImpl : IUserService {
override fun getCurrentUser(): User? = UserManager.currentUser
override fun logout() { /* ... */ }
}

// 通过 ServiceLocator 或依赖注入框架注入
object ServiceLocator {
var userService: IUserService? = null
}

// module_order 中调用
class OrderActivity : AppCompatActivity() {
private val userService: IUserService? by lazy { ServiceLocator.userService }

fun showUserInfo() {
userService?.getCurrentUser()?.let { user ->
// 显示用户信息
}
}
}

三、组件化原理与实现

3.1 组件化架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
                ┌──────────────────┐
│ app (壳) │
│ 主工程/组装 │
└────────┬─────────┘

┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ module_user │ │ module_order│ │module_payment│
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────┼───────────────┘

┌──────▼──────┐
│ ARouter │
│ 路由中间层 │
└──────┬──────┘

┌──────▼──────┐
│ common_base │
│ 基础组件库 │
└─────────────┘

3.2 路由方案对比

方案 原理 优点 缺点
ARouter 注解 + 编译期生成路由表 阿里出品、生态成熟、支持拦截器 需引入注解处理器
WMRouter 美团方案,分组路由 按需加载、包体积优化 学习成本略高
CC (Component Call) 组件调用框架 同步/异步调用、跨进程 概念较多
手写路由表 Map 映射 简单、无依赖 维护成本高

3.3 单模块独立运行(Application 切换)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// module_user/build.gradle
android {
defaultConfig {
// 作为 Application 运行时使用
if (project.hasProperty('runAsApp') && runAsApp) {
applicationId "com.example.user"
}
}
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
if (project.hasProperty('runAsApp') && runAsApp) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- module_user/src/main/debug/AndroidManifest.xml - 独立运行时的 Manifest -->
<manifest>
<application
android:name=".UserDebugApplication"
android:label="User Module Debug">
<activity android:name=".UserActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
1
2
# 独立运行用户模块
./gradlew :module_user:installDebug -PrunAsApp=true

3.4 ARouter 使用与原理

3.4.1 添加依赖

1
2
3
4
5
// 各模块 build.gradle
dependencies {
implementation 'com.alibaba:arouter-api:1.5.2'
kapt 'com.alibaba:arouter-compiler:1.5.2'
}

3.4.2 路由配置与跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ========== 组件端:Order 模块 ==========
// 在 OrderListActivity 上添加路由注解
@Route(path = "/order/list")
class OrderListActivity : AppCompatActivity() {
@Autowired
lateinit var userId: String

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ARouter.getInstance().inject(this)
// userId 已自动注入
}
}

// ========== 调用端:任意模块 ==========
ARouter.getInstance()
.build("/order/list")
.withString("userId", "12345")
.navigation()

3.4.3 服务发现(跨模块获取实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义接口(可在 common_base 或单独 api 模块)
interface IOrderService {
fun getOrderCount(userId: String): Int
}

// Order 模块实现并注册
@Route(path = "/service/order")
class OrderServiceImpl : IOrderService {
override fun getOrderCount(userId: String): Int = /* ... */
}

// 调用端获取
val orderService = ARouter.getInstance()
.navigation(IOrderService::class.java)
orderService?.getOrderCount("12345")

3.4.5 ARouter 核心源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 简化版 ARouter 核心逻辑
// 1. 初始化时扫描 Dex,加载路由表(通过 Gradle 插件编译期生成)
class Warehouse {
// 路由表:path -> RouteMeta
static Map<String, RouteMeta> routes = ConcurrentHashMap()
// 分组缓存
static Map<String, Class<? extends IRouteGroup>> groupsIndex = ConcurrentHashMap()
}

// 2. navigation 流程
fun navigation(path: String): Postcard {
val meta = Warehouse.routes[path] // 从路由表获取
return Postcard(path, meta)
}

fun Postcard.navigation(): Any? {
return _ARouter.navigation(context, this, requestCode, navigationCallback)
}

// 3. 实际跳转
fun _ARouter.navigation(...) {
when (meta.type) {
RouteType.ACTIVITY -> {
val intent = Intent(context, meta.destination)
// 注入参数
context.startActivity(intent)
}
RouteType.ISERVICE -> {
return meta.destination.newInstance() // 服务实现类
}
}
}

// 4. 编译期注解处理器生成路由表
// 生成类似:ARouter$$Group$$order、ARouter$$Providers$$order
// 在 init() 时通过反射加载到 Warehouse

3.5 依赖关系设计原则

1
2
3
4
5
6
7
8
9
业务模块 (user/order/payment)

├── 只依赖 common_base、common_router (arouter-api)

└── 业务模块之间 不直接依赖

app 壳工程

└── 依赖所有业务模块,负责组装和 Application 初始化

四、插件化原理与实现

4.1 插件化的应用场景

  • 热更新:修复线上 Bug 无需发版
  • 按需加载:减小包体积,冷启动只加载核心
  • 动态能力:运营活动插件、A/B 测试模块
  • 多端复用:同一套插件可被主 App、Split APK 加载

4.2 Android 插件化技术选型

方案 说明 适用场景
Dynamic Feature Modules Google 官方,AGP 支持 按需下载、免安装功能模块
VirtualAPK 滴滴开源 完整的插件化框架
RePlugin 360 出品 插件即独立 APK,稳定成熟
Shadow 腾讯开源 支持 Android 10+,零 Hook
Atlas 阿里 大型 App 动态化

4.3 Dynamic Feature Modules(官方方案)

4.3.1 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// settings.gradle
include ':app'
include ':feature:order' // 动态功能模块

// app/build.gradle
dependencies {
implementation project(':common_base')
dynamicFeatures = [':feature:order']
}

// feature/order/build.gradle
apply plugin: 'com.android.dynamic-feature'
android {
defaultConfig {
minSdk 24
}
}
dependencies {
implementation project(':app')
}

4.3.2 按需下载与安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用 Play Core Library 下载动态模块
val splitInstallManager = SplitInstallManagerFactory.create(context)
val request = SplitInstallRequest.newBuilder()
.addModule("order")
.build()

splitInstallManager.startInstall(request)
.addOnSuccessListener {
// 安装成功,可跳转
startActivity(Intent(this, OrderActivity::class.java))
}
.addOnFailureListener {
// 处理失败
}

4.4 RePlugin 核心原理简述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────────────────┐
│ Host App │
│ ┌────────────────────────────────────────────────┐ │
│ │ RePluginHostConfig / PluginManager │ │
│ │ - 加载插件 APK │ │
│ │ - 替换 ClassLoader │ │
│ │ - Hook Activity/Service 等组件 │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘

┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Plugin1 │ │ Plugin2 │ │ Plugin3 │
│ .apk │ │ .apk │ │ .apk │
└──────────┘ └──────────┘ └──────────┘

核心流程

  1. Dex 加载:将插件 APK 的 classes.dex 加入宿主的 DexPathList
  2. 资源加载:创建 Resources,合并插件的 AssetManager
  3. 组件占位:通过 Hook Instrumentation/IActivityManager,用占位 Activity 代理插件 Activity

4.5 手写简易插件加载(Dex 加载)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 简化示例:加载插件 APK 中的类
class PluginLoader(private val context: Context) {

private val pluginClassLoaders = mutableMapOf<String, DexClassLoader>()

fun loadPlugin(apkPath: String): Boolean {
val optimizedDir = File(context.filesDir, "plugin_opt").apply { mkdirs() }
val libDir = File(context.filesDir, "plugin_lib").apply { mkdirs() }

val classLoader = DexClassLoader(
apkPath,
optimizedDir.absolutePath,
null,
context.classLoader
)
pluginClassLoaders[apkPath] = classLoader
return true
}

fun loadClass(apkPath: String, className: String): Class<*>? {
val loader = pluginClassLoaders[apkPath] ?: return null
return try {
loader.loadClass(className)
} catch (e: ClassNotFoundException) {
null
}
}
}

// 加载插件中的 Activity(实际还需 Hook 组件启动流程)
val loader = PluginLoader(context)
loader.loadPlugin("/sdcard/plugin.apk")
val clazz = loader.loadClass("/sdcard/plugin.apk", "com.plugin.MainActivity")
val activity = clazz.newInstance()

4.6 插件化资源加载

1
2
3
4
5
6
7
8
9
10
11
12
// 合并插件资源到宿主
fun createPluginResources(hostResources: Resources, apkPath: String): Resources {
val assets = AssetManager::class.java.newInstance()
val addAssetPath = AssetManager::class.java.getMethod("addAssetPath", String::class.java)
addAssetPath.invoke(assets, apkPath)

return Resources(
assets,
hostResources.displayMetrics,
hostResources.configuration
)
}

五、ARouter 框架源码深度解析

5.1 整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌────────────────────────────────────────────────────────┐
│ ARouter │
│ - 对外 API:build、navigation、inject │
└────────────────────────────────────────────────────────┘

┌─────────────────────────┴─────────────────────────────┐
│ _ARouter (内部实现) │
│ - LogisticsCenter:路由表、分组加载 │
│ - Warehouse:路由/服务/拦截器 存储 │
└────────────────────────────────────────────────────────┘

┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 注解处理器 │ │ 拦截器链 │ │ 降级策略 │
│ 编译期生成 │ │ 可扩展 │ │ DegradeService│
└──────────────┘ └──────────────┘ └──────────────┘

5.2 LogisticsCenter 路由表加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 初始化时通过插件扫描 Dex 中的特定类
public static void init(Context context) {
try {
// 获取所有 ARouter 生成的类:ARouter$$Root$$xxx
Set<String> routerMap = ClassUtils.getFileNameByPackageName(
context, ROUTE_ROOT_PACKAGE);
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PACKAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) Class.forName(className).getConstructor().newInstance())
.loadInto(Warehouse.groupsIndex);
}
}
// 分组按需加载,避免启动时加载全部
} catch (Exception e) {
throw new HandlerException("ARouter init logistics center exception");
}
}

5.3 拦截器机制

1
2
3
4
5
6
7
8
9
10
11
12
@Interceptor(priority = 8)
class LoginInterceptor : IInterceptor {
override fun process(postcard: Postcard, callback: InterceptorCallback) {
if (postcard.extra == NEED_LOGIN && !UserManager.isLoggedIn()) {
// 未登录,拦截并跳转登录
ARouter.getInstance().build("/user/login").navigation()
callback.onInterrupt(null)
} else {
callback.onContinue(postcard)
}
}
}

六、实际项目应用案例

6.1 某电商 App 组件化拆分

1
2
3
4
5
6
7
8
9
10
11
app (壳工程)
├── module_home # 首页
├── module_product # 商品详情
├── module_cart # 购物车
├── module_order # 订单
├── module_user # 用户中心
├── module_payment # 支付
├── module_share # 分享(可复用)
├── module_analytics # 埋点(可复用)
├── common_router # ARouter 封装
└── common_base # 网络/缓存/UI 基础库

收益

  • 编译时间:全量 6min → 单模块 1min
  • 4 个业务线可并行开发,Git 冲突减少 60%
  • ShareModule、AnalyticsModule 复用到多个 App

6.2 路由表设计实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 统一路由 Path 常量
object RouterPath {
const val ORDER_LIST = "/order/list"
const val ORDER_DETAIL = "/order/detail"
const val USER_LOGIN = "/user/login"
const val USER_PROFILE = "/user/profile"
}

// 封装便捷方法
object Router {
fun toOrderList(userId: String) {
ARouter.getInstance()
.build(RouterPath.ORDER_LIST)
.withString("userId", userId)
.navigation()
}

fun toOrderDetail(orderId: String) {
ARouter.getInstance()
.build(RouterPath.ORDER_DETAIL)
.withString("orderId", orderId)
.navigation()
}
}

6.3 解耦实践:避免循环依赖

错误示例

1
2
module_order -> module_user (获取用户信息)
module_user -> module_order (跳转订单列表)

形成循环依赖,Gradle 编译失败。

正确做法

1
2
module_order -> common_router
module_user -> common_router

需要「用户信息」时,Order 通过 ARouter.getInstance().navigation(IUserService::class.java) 获取;需要「跳转订单」时,User 通过 ARouter.getInstance().build("/order/list").navigation() 跳转。

6.4 模块化 + 插件化组合:Dynamic Feature 实践

某资讯 App 将「小说」「漫画」等非核心功能做成 Dynamic Feature,用户首次进入时提示下载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检查模块是否已安装
fun isModuleInstalled(moduleName: String): Boolean {
return SplitInstallManagerFactory.create(context)
.installedModules.contains(moduleName)
}

// 进入小说模块前检查
fun openNovelModule() {
if (isModuleInstalled("novel")) {
startActivity(Intent(this, NovelActivity::class.java))
} else {
showDownloadDialog {
installDynamicModule("novel")
}
}
}

七、最佳实践与注意事项

7.1 模块拆分原则

  • 高内聚低耦合:模块内聚度高,模块间依赖少
  • 按业务边界拆分:参考 DDD 的 Bounded Context
  • 基础组件下沉:网络、缓存、日志等抽成 common_base
  • 渐进式演进:先模块化再组件化,避免一步到位导致成本过高

7.2 常见坑与规避

问题 原因 规避
编译顺序错误 模块间隐式依赖 严格检查 build.gradle,用 ./gradlew :module_x:dependencies 检查
路由表膨胀 所有页面都注册路由 仅对外暴露的页面注册,内部页面用 startActivity
启动变慢 初始化时加载全部路由 使用分组按需加载(ARouter 已支持)
R 文件冲突 多模块资源 ID 冲突 在 build.gradle 中设置 resourcePrefix "module_user_"
插件化兼容性 不同厂商 ROM 限制 优先使用 Dynamic Feature,第三方框架需充分测试

7.3 R 文件与资源隔离

1
2
3
4
// 各业务模块 build.gradle 中
android {
resourcePrefix "user_" // 资源必须以 user_ 开头
}

7.4 架构演进路线图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Phase 1: 模块化
├── 拆分为 Gradle 多模块
├── 建立 common_base、common_router
└── 模块间通过接口通信

Phase 2: 组件化
├── 引入 ARouter 路由
├── 服务发现替代直接依赖
└── 支持单模块独立运行调试

Phase 3: 插件化(可选)
├── 非核心功能改为 Dynamic Feature
├── 或引入 RePlugin/Shadow 做完整插件化
└── 按需下载、热更新

八、总结

架构 适用场景 核心手段
模块化 中小型 App、团队 < 10 人 Gradle 多模块、接口、依赖配置
组件化 中大型 App、多业务线并行 ARouter、服务发现、单模块调试
插件化 需要动态化、热更新、包体积优化 Dynamic Feature、RePlugin、Shadow

架构没有银弹,需结合团队规模、业务复杂度、迭代节奏选择合适方案。建议从模块化起步,随着复杂度提升再逐步演进到组件化,插件化则按实际需求谨慎引入。


参考资源

Flutter 主流状态管理库:选型、原理与源码导读

本文从工程选型出发,对比 Flutter 生态中常用的状态管理方案,并给出实现思路与源码阅读入口,便于结合官方仓库深入学习。

目录


一、如何理解「状态管理」在 Flutter 中的位置

Flutter 的 UI 由 **Widget 配置树** 描述;真正随数据变化而刷新的,依赖 **Element / RenderObject** 的更新机制。状态管理解决的是:把业务数据放在哪、如何通知依赖它的 Widget 重建、如何组织可测试的业务逻辑。

选型时通常看:学习曲线、样板代码量、与测试/依赖注入的契合度、团队习惯、是否强约束数据流


二、设计思想与经典模式

状态管理库里的常见做法,多半能在经典设计思想里找到落点:谁在存状态、谁订阅变化、谁负责把变化交给 UI。下面是与本文各方案最相关的几种;它们在实际工程里往往组合出现,而不是非此即彼。

2.1 观察者模式(Observer)

含义:被观察对象(Subject)在状态变化时通知一组观察者(Observer),由观察者决定如何更新。
在 Flutter 里Listenable / ChangeNotifier 是典型的「可订阅 + 广播通知」;ValueNotifier 是带当前值的特化。notifyListeners() 对应 Subject 发通知ValueListenableBuilder / AnimatedBuilder 等对应 Observer 侧的重建入口
与各方案:Provider + ChangeNotifier、以及 UI 侧订阅 Stream<State>BlocBuilder,都体现观察者思想;Bloc 一侧更强调 随时间展开的异步事件流

2.2 发布—订阅(Pub/Sub)与响应式流

与观察者同属「一对多通知」,但常强调 发送方与订阅方解耦、中间可有 异步缓冲Stream / StreamController、Bloc 的 Stream<State>、Riverpod 的 ref.listen(副作用订阅)都可归入这一族:状态或事件沿时间轴推送给订阅者,便于表达多步异步与管线化逻辑(Bloc 尤为典型)。

2.3 依赖注入(DI)与服务定位器(Service Locator)

含义:由外部组装依赖,调用方依赖抽象而非在内部直接 new 具体实现,便于替换与测试。
在 Flutter 里InheritedWidget、Provider、Riverpod 的 ProviderScope + ref.read,以及 GetX 的 Get.put / Get.find,都在做「把实现挂到某处,子树或全局按类型 / key 取用」。差异主要在生命周期(是否与 Element 树绑定、是否全局单例)以及 Riverpod 带来的 编译期与依赖图层面的安全

2.4 命令模式(Command)与显式状态(Bloc)

Bloc 中的 **Event 可视为命令对象**:UI 不直接改领域状态,而是派发事件,由 Bloc 统一解释并 emitState。再配合不可变、可穷举的 State 类型描述界面阶段,相当于把业务规则收拢到「命令处理 + 状态迁移」,利于单测与行为审计。Cubit 则是用 方法调用 代替显式 Event 类型,思想仍相近。

2.5 外观(Facade)、组合根与依赖倒置

InheritedWidget 手写成本高,Provider 等库在其上提供更易用的 Facade 式 APIRiverpodProviderContainer 把依赖图从整棵 BuildContext 树里抽出一层,更接近在应用入口做 组合根(Composition Root) 集中装配。分层架构里常配合 依赖倒置:领域层定义接口,具体实现由外层注入——与上述 DI 能力天然契合。

2.6 各方案与上述思想的对应(速查)

方案 / API 主要涉及的设计思想
setState 命令式更新;框架 脏标记 驱动单棵子树重建
ValueNotifier + ValueListenableBuilder 观察者Listenable
Provider + ChangeNotifier DI / 服务定位InheritedWidget 查找)+ 观察者notifyListeners
Riverpod 组合根 + 依赖图 + 依赖追踪下的观察者式失效ref.watch
Bloc / Cubit 命令(Event)或方法入口 + 显式 State + Pub/Sub 式 Stream
GetX 服务定位 / 全局注入 + 响应式更新(具体可测试性高度依赖团队约定)

理清这些对应关系,再读各库的 watchlistenreademit 等 API,会少很多「名不同、实相近」的困惑。


三、内置与轻量方案

3.1 setState

  • 优点:零依赖,心智负担小,适合局部、短命状态。
  • 缺点:逻辑与 UI 耦在 State 里,规模一大难以拆分与单测。
  • 适用:页面内表单开关、动画控制器等。
  • 原理setState 标记 Element dirty,在下一帧 build 中重建子树。

3.2 ValueNotifier + ValueListenableBuilder

  • 优点:对象级可监听,比 setState 更易抽到类字段中。
  • 缺点:跨页面传递需手动层层传参或自己封装 InheritedWidget
  • 适用:单页面内一块 UI 依赖单个标量/小对象。
  • 原理Listenable 通知监听者;ValueListenableBuilderbuild 中注册监听并重建。

四、Provider

定位:在 InheritedWidget 之上封装「依赖查找 + 更新传播」,常与 **ChangeNotifier** 搭配。

维度 说明
优点 官方文档与示例多;API 面相对小;MultiProvider 组合清晰;与 ChangeNotifier 成熟。
缺点 大型应用中 ChangeNotifier 类易膨胀;依赖树与 BuildContext 绑定,错误 context 易导致找不到 Provider。
适用 中小型 App、从入门到中等复杂度的业务模块。
原理要点 Provider/Consumer 等通过 InheritedWidget 向下提供值;ChangeNotifier 通知时触发 notifyListeners(),依赖的 Consumer/context.watch 重建。

五、Riverpod

定位:Provider 作者推出的「下一代」,编译期安全 + 全局注册表,弱化对 BuildContext 的依赖(ref 为主)。

维度 说明
优点 类型与依赖关系更清晰;Provider 可组合、覆盖测试;异步/家族参数(family)表达力强。
缺点 概念多(NotifierAsyncNotifier、代码生成可选);团队需统一规范。
适用 中大型项目、需要可测试依赖图与清晰模块边界的团队。
原理要点 **ProviderContainer** 持有所有 Provider 的状态;ref.watch/read/listen 建立依赖;更新时按依赖图失效下游。代码生成路径下由 **riverpod_generator** 生成 `.g.dart*。

六、Bloc / Cubit(flutter_bloc)

定位事件驱动 + 显式状态转移CubitBloc 的简化(方法调用代替 Event 类型)。

维度 说明
优点 数据流单向、状态机思维清晰;时间旅行调试(BlocObserver);与分层架构、单测契合好。
缺点 样板代码多(Event/State 类);小功能也显重。
适用 复杂业务流、多分支异步、需严格审计状态变化的场景(支付、登录流程、长表单步骤)。
原理要点 Bloc 接收 Event,在 mapEventToState(或新版 handler)中 **emitStateBlocBuilder/BlocListener 订阅 Stream<State>;底层基于 **Stream + Sink 与协调调度。

七、GetX(简述)

维度 说明
优点 路由、依赖注入、状态一套 API,上手快;写法省代码。
缺点 与 Flutter 官方推荐的数据流模式差异大;社区对「隐式全局」与可测试性争议多;大版本与迁移成本需自行评估。
适用 快速原型、小团队强约定场景;企业级长期维护前建议充分评估。

八、横向对比与适用场景

方案 学习成本 样板代码 可测试性 与官方范式契合
setState / ValueNotifier 中(看拆分)
Provider + ChangeNotifier 低~中 中高
Riverpod 中~高 中(+ 生成器)
Bloc/Cubit
GetX 低~中 视用法而定

场景建议(概括)

  • 页面内小状态setState / ValueNotifier
  • 多页面共享、中等规模ProviderRiverpod(更看团队是否愿意引入 Riverpod 心智模型)。
  • 强流程、多事件、要画清状态机Bloc/Cubit
  • 极快交付且团队接受其风格:可考察 GetX,并做好规范与评审。

九、实现原理归纳

  1. 依赖向下、通知向上InheritedWidget / Provider / Riverpod 本质是「子树查找 + 依赖追踪」。
  2. Listenable / StreamChangeNotifierBloc 分别代表 拉模型监听推模型流 两类更新机制。
  3. 刷新粒度:从「整页 setState」到「Selector/select 只重建一部分」,是性能与 API 设计的共同主题。
  4. Riverpod:把「谁依赖谁」放在 容器里统一管理,而不是仅靠 BuildContext 树,这是和经典 Provider 的重要区别。
  5. 与经典模式的对照:观察者、Listenable / Stream 两类通知、依赖注入与组合根等,在 第二节 已从「设计思想」角度归纳,可与上四条穿插阅读。

十、源码导读:从哪几个文件读起

下面列出 Pub 上包名 与建议阅读顺序(以 GitHub 托管为准,分支以主分支为例)。阅读时结合你项目里 pubspec.yaml 锁定的版本更佳。

10.1 provider

  • 仓库:flutter/packages 中的 **provider** 包(或社区维护的 provider 独立仓库,以 pub.dev 主页为准)。
  • 入口lib/src/provider.dartinherited_provider.dart — 看 InheritedWidget 如何包装 value / delegate
  • ChangeNotifier 集成change_notifier_provider.dartListenableElement 更新如何衔接。

10.2 flutter_riverpod / riverpod

  • 仓库:**rrousselGit/riverpod**。
  • 核心lib/src/framework/container.dartProviderContainer)、provider/base.dart 一带 — 理解 Provider 状态存哪、如何失效
  • 注解与生成:若用代码生成,配合阅读 **riverpod_generator** 生成的 *.g.dartriverpod_annotation

10.3 bloc / flutter_bloc

  • 仓库:**felangel/bloc**。
  • **bloc 包**:lib/src/bloc.dartBloc 基类、on<Event>emitStream 管道。
  • **flutter_bloc 包**:bloc_builder.dartbloc_listener.dart — 如何把 Stream 接到 Widget 树。

10.4 Flutter 框架层(通用基础)

  • flutter/packages/flutter/lib/src/widgets/framework.dart**ElementInheritedElement** 更新机制。
  • inherited_model.dartnotification_listener.dart — 与自定义状态传播相关时可查。

10.5 阅读方法建议

  1. 先在你自己的 Demo 里 打一个断点,从 Consumer / ref.watch / BlocBuilderbuild 跟进去。
  2. 对照本文「原理要点」只读一条主路径,不要一上来通读全仓库。
  3. 版本以 **pubspec.lock** 为准,避免文档与旧版 API 不一致。

小结

没有「唯一正确」的库:小项目用内置 + Provider 往往足够;大项目更常见 RiverpodBloc 与清晰分层结合。先扫一眼 第二节 里的观察者、Pub/Sub、DI、命令等表述,再对照 InheritedWidgetListenableStream 三条技术线,读各库 API 与源码会省力很多。若你后续锁定某一库做深度拆解,可以在此基础上单独成文(例如只讲 Bloc 的并发与 emit 规则)。

iOS 模块化、组件化、插件化架构

从基础概念到实战应用,系统梳理 iOS 架构演进之路


一、基础概念

1.1 为什么需要架构优化?

随着业务迭代,单体 App 会遇到诸多问题:

问题 表现 影响
代码耦合严重 模块间直接 import,循环依赖 难以维护、编译慢
编译效率低 改一行代码全量编译 开发效率下降
团队协作冲突 多人修改同一工程 Git 冲突频繁
无法独立开发 强依赖主工程 无法并行开发、独立测试
复用困难 业务逻辑与 UI 混在一起 跨项目复用成本高

1.2 三种架构模式辨析

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────────────────────────────────┐
│ 架构演进路径 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 单体架构 ──► 模块化 ──► 组件化 ──► 插件化 │
│ │ │ │ │ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 按功能拆分 物理隔离 独立仓库 运行时动态加载 │
│ 代码目录 组件解耦 CocoaPods 热更新/按需加载 │
│ │
└─────────────────────────────────────────────────────────────────┘

模块化(Modularization)

  • 定义:按业务功能将代码拆分为独立的「模块」,每个模块有清晰的边界
  • 特点:逻辑划分、职责单一,通常仍在同一工程内
  • 粒度:中等,如「用户模块」「订单模块」「支付模块」

组件化(Componentization)

  • 定义:将模块进一步拆分为可独立编译、可复用的「组件」,通过依赖注入解耦
  • 特点:物理隔离、独立仓库、独立编译、协议解耦
  • 粒度: finer,如「登录组件」「分享组件」「埋点组件」

插件化(Pluginization)

  • 定义:组件可动态加载、热更新,主 App 与插件解耦到运行时
  • 特点:运行时动态、按需加载、可热修复
  • 粒度:动态 Bundle/Framework

二、模块化原理与实践

2.1 模块化的核心原则

  1. 单一职责:每个模块只负责一块业务
  2. 接口隔离:模块间通过 Protocol 通信,不暴露实现细节
  3. 依赖倒置:依赖抽象(协议)而非具体实现

2.2 目录结构示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MyApp/
├── App/ # 主工程壳
│ ├── AppDelegate
│ └── Main
├── Modules/
│ ├── ModuleA_User/ # 用户模块
│ │ ├── Services/
│ │ ├── Views/
│ │ └── Models/
│ ├── ModuleB_Order/ # 订单模块
│ └── ModuleC_Payment/ # 支付模块
├── Common/ # 公共基础库
│ ├── Network/
│ ├── Utils/
│ └── BaseClasses/
└── Router/ # 路由层(模块间通信枢纽)

2.3 模块间通信:Protocol + 依赖注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 1. 定义协议(放在公共层或 Protocol 模块)
protocol UserServiceProtocol: AnyObject {
func getCurrentUser() -> User?
func logout()
}

// 2. 模块实现协议
// UserModule/UserService.swift
class UserService: UserServiceProtocol {
func getCurrentUser() -> User? { /* ... */ }
func logout() { /* ... */ }
}

// 3. 依赖注入(App 启动时或 ServiceLocator)
class ServiceLocator {
static let shared = ServiceLocator()
var userService: UserServiceProtocol?
}

// 4. 其他模块通过协议调用
class OrderViewController: UIViewController {
var userService: UserServiceProtocol?

func showUserInfo() {
guard let user = userService?.getCurrentUser() else { return }
// ...
}
}

三、组件化原理与实现

3.1 组件化架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
                ┌──────────────────┐
│ App Shell │
│ (主工程/壳工程) │
└────────┬─────────┘

┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户组件 │ │ 订单组件 │ │ 支付组件 │
│ (Pod) │ │ (Pod) │ │ (Pod) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────┼───────────────┘

┌──────▼──────┐
│ 路由/中间层 │
│ (URL/Mediator)│
└──────┬──────┘

┌──────▼──────┐
│ 基础组件 │
│ (网络/缓存/UI)│
└─────────────┘

3.2 路由方案对比

方案 原理 优点 缺点
URL Router 字符串 URL 映射到 VC 简单、支持 H5 跳转 参数传递不便、类型不安全
Target-Action (Mediator) 通过 Mediator 类反射调用 解耦彻底、类型安全 需维护中间类
Protocol 协议注册 + 实现查找 接口清晰 需集中注册

3.3 Mediator 模式源码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// CTMediator 核心实现(简化版,源自 Casa 的 CTMediator)
import UIKit

public class CTMediator: NSObject {
public static let shared = CTMediator()

// 缓存 Target 实例,避免重复创建
private var targetCache = [String: NSObject]()

/// 通过 Target-Action 调用
/// - Parameters:
/// - targetName: 目标类名,如 "Order"
/// - actionName: 方法名,如 "orderListViewController"
/// - params: 参数字典
public func perform(target targetName: String,
action actionName: String,
params: [String: Any]? = nil) -> Any? {
let targetClassString = "Target_\(targetName)"
let actionString = "Action_\(actionName):"

// 1. 获取 Target 类
guard let targetClass = NSClassFromString(targetClassString) as? NSObject.Type else {
return nil
}

// 2. 获取或创建 Target 实例
var target = targetCache[targetClassString]
if target == nil {
target = targetClass.init()
targetCache[targetClassString] = target
}

// 3. 通过 NSInvocation 或 perform 调用
let selector = NSSelectorFromString(actionString)
guard (target?.responds(to: selector)) ?? false else {
return nil
}

return target?.perform(selector, with: params)?.takeUnretainedValue()
}
}

// ========== 组件端:Order 模块 ==========
// 在 Order 组件内定义 Target_Order
class Target_Order: NSObject {
@objc func Action_orderListViewController(_ params: [String: Any]?) -> UIViewController {
let userId = params?["userId"] as? String ?? ""
let vc = OrderListViewController(userId: userId)
return vc
}
}

// ========== 调用端:任意模块 ==========
let vc = CTMediator.shared.perform(
target: "Order",
action: "orderListViewController",
params: ["userId": "12345"]
) as? UIViewController
navigationController?.pushViewController(vc!, animated: true)

3.4 依赖关系设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Podfile 示例
target 'MainApp' do
# 业务组件(同层,互不依赖)
pod 'UserModule'
pod 'OrderModule'
pod 'PaymentModule'

# 中间层
pod 'Mediator'
pod 'Mediator/Order' # Category,扩展 Order 的便捷方法
pod 'Mediator/User'
end

# 各业务组件的 Podspec 只依赖基础库
# OrderModule.podspec
Pod::Spec.new do |s|
s.name = 'OrderModule'
s.dependency 'Mediator'
s.dependency 'BaseNetwork'
s.dependency 'BaseUI'
end

关键点:业务组件之间不直接依赖,都通过 Mediator 间接通信。


四、插件化原理与实现

4.1 插件化的应用场景

  • 热更新:修复线上 Bug 无需发版
  • 按需加载:减少包体积,冷启动只加载核心
  • 动态能力:运营活动插件、A/B 测试模块
  • 多端复用:同一套插件可被主 App、Widget、Watch 加载

4.2 iOS 插件化技术选型

技术 说明 限制
Dynamic Framework 动态库,可延迟加载 App Store 限制主二进制外的动态库
Bundle + 反射 资源/代码打包成 .bundle,运行时加载 无法绕过沙盒,需提前打包进 App
JavaScript 引擎 React Native / Flutter / JSI 非原生,性能与体验有差异
JSPatch / 热修复 通过 Runtime 动态替换方法 已不可上架 App Store

注意:苹果审核禁止下载执行任意代码,真正「从网络下载插件并执行」的纯插件化在 App Store 场景不可行。实际做法多为:预置多个 Framework/Bundle,运行时按需加载,或通过 JS 引擎 + 离线包 实现业务热更新。

4.3 动态 Framework 加载示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import UIKit

class PluginManager {
static let shared = PluginManager()
private var loadedBundles: [String: Bundle] = [:]

/// 加载动态 Framework(需预置在 App 内)
func loadPlugin(named pluginName: String) -> Bool {
guard let path = Bundle.main.path(
forResource: pluginName,
ofType: "framework",
inDirectory: "Frameworks"
) else { return false }

guard let bundle = Bundle(path: path), bundle.load() else {
return false
}

loadedBundles[pluginName] = bundle
return true
}

/// 通过反射获取插件内的类并调用
func instantiateViewController(fromPlugin pluginName: String,
className: String) -> UIViewController? {
guard let bundle = loadedBundles[pluginName] ?? {
loadPlugin(named: pluginName) ? loadedBundles[pluginName] : nil
}() else { return nil }

guard let pluginClass = bundle.classNamed(className) as? UIViewController.Type else {
return nil
}

return pluginClass.init()
}
}

// 使用
if PluginManager.shared.loadPlugin(named: "ActivityPlugin") {
let vc = PluginManager.shared.instantiateViewController(
fromPlugin: "ActivityPlugin",
className: "ActivityPlugin.ActivityViewController"
)
present(vc!, animated: true)
}

4.4 基于 Runtime 的模块注册

1
2
3
4
5
6
7
8
9
10
11
12
// 插件自注册:通过 +load 在加载时注册到中心
// PluginModule.m
+ (void)load {
[[PluginRegistry shared] registerPlugin:@"Activity"
withClass:[ActivityPlugin class]];
}

// PluginRegistry 管理所有插件
@interface PluginRegistry : NSObject
- (void)registerPlugin:(NSString *)name withClass:(Class)cls;
- (id)createInstanceForPlugin:(NSString *)name;
@end

五、BeeHive 框架源码解析

BeeHive 是阿里开源的 iOS 模块化框架,结合了 Protocol 注册Module 生命周期

5.1 架构概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌────────────────────────────────────────────────────────┐
│ BHContext (上下文) │
│ - 配置信息、环境变量、共享数据 │
└────────────────────────────────────────────────────────┘

┌─────────────────────────┴─────────────────────────────┐
│ BHModuleManager │
│ - 管理 Module 注册、加载、生命周期 │
└────────────────────────────────────────────────────────┘

┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ BHService │ │ BHModule │ │ BHConfig │
│ (协议-实现) │ │ (模块基类) │ │ (配置) │
└──────────────┘ └──────────────┘ └──────────────┘

5.2 核心类:BHModuleManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 简化版核心逻辑
@implementation BHModuleManager

- (void)registerDynamicModule:(Class)moduleClass {
if ([moduleClass conformsToProtocol:@protocol(BHModuleProtocol)]) {
BHModuleInfo *info = [[BHModuleInfo alloc] initWithModuleClass:moduleClass];
[self.modules addObject:info];
[self.modules sortUsingComparator:^NSComparisonResult(BHModuleInfo *m1, BHModuleInfo *m2) {
return m1.priority < m2.priority; // 按优先级排序
}];
}
}

- (void)triggerEvent:(NSInteger)eventType {
for (BHModuleInfo *info in self.modules) {
id<BHModuleProtocol> instance = [info moduleInstance];
switch (eventType) {
case BHMSetupEvent:
[instance modSetUp:nil];
break;
case BHMInitEvent:
[instance modInit:nil];
break;
case BHMSplashEvent:
[instance modSplash:nil];
break;
// ... 更多生命周期事件
}
}
}

@end

5.3 Protocol 注册与查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// BHServiceManager:Protocol -> 实现类 映射
- (void)registerService:(Protocol *)protocol implClass:(Class)implClass {
NSString *key = NSStringFromProtocol(protocol);
self.services[key] = implClass;
}

- (id)createService:(Protocol *)protocol {
Class implClass = self.services[NSStringFromProtocol(protocol)];
return [[implClass alloc] init];
}

// 使用
@protocol UserServiceProtocol <NSObject>
- (User *)currentUser;
@end

// 注册
[[BHServiceManager sharedManager] registerService:@protocol(UserServiceProtocol)
implClass:[UserServiceImpl class]];

// 获取
id<UserServiceProtocol> service = [[BHServiceManager sharedManager] createService:@protocol(UserServiceProtocol)];

六、实际项目应用案例

6.1 某电商 App 组件化拆分

1
2
3
4
5
6
7
8
9
10
11
MainApp (壳工程)
├── HomeModule # 首页
├── ProductModule # 商品详情
├── CartModule # 购物车
├── OrderModule # 订单
├── UserModule # 用户中心
├── PaymentModule # 支付
├── ShareModule # 分享(可复用)
├── AnalyticsModule # 埋点(可复用)
├── Mediator # 路由中间层
└── BasePods # 网络/缓存/UI 基础库

收益

  • 编译时间:全量 8min → 单模块 1.5min
  • 4 个业务线可并行开发,Git 冲突减少 70%
  • ShareModule、AnalyticsModule 复用到多个 App

6.2 路由设计实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Mediator+Order.swift (Mediator 的 Category)
extension CTMediator {
func orderListViewController(userId: String) -> UIViewController? {
return perform(target: "Order",
action: "orderListViewController",
params: ["userId": userId]) as? UIViewController
}

func orderDetailViewController(orderId: String) -> UIViewController? {
return perform(target: "Order",
action: "orderDetailViewController",
params: ["orderId": orderId]) as? UIViewController
}
}

// 调用处:无需关心 Order 模块内部实现
if let vc = CTMediator.shared.orderListViewController(userId: "123") {
navigationController?.pushViewController(vc, animated: true)
}

6.3 解耦实践:避免循环依赖

错误示例

1
2
OrderModule -> UserModule (获取用户信息)
UserModule -> OrderModule (跳转订单列表)

形成循环依赖,无法独立编译。

正确做法

1
2
OrderModule -> Mediator
UserModule -> Mediator

需要「用户信息」时,Order 通过 Mediator 获取 UserServiceProtocol;需要「跳转订单」时,User 通过 Mediator 获取 OrderListViewController。Mediator 依赖各模块的 Target 类(可通过 Category 按需引入)。


七、最佳实践与注意事项

7.1 模块拆分原则

  • 高内聚低耦合:模块内聚度高,模块间依赖少
  • 按业务边界拆分:参考 DDD 的 Bounded Context
  • 基础组件下沉:网络、缓存、日志等抽成独立 Pod
  • 渐进式演进:先模块化再组件化,避免一步到位导致成本过高

7.2 常见坑与规避

问题 原因 规避
编译顺序错误 组件间隐式依赖 严格检查 Pod 依赖,用 pod lib lint 校验
协议爆炸 过度抽象 只对跨模块通信定义协议,模块内部保持自由
路由表膨胀 所有页面都走路由 仅对外暴露的页面注册路由
启动变慢 Module 过多,+load 耗时 延迟注册、按需加载、控制 Module 数量

7.3 架构演进路线图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Phase 1: 模块化
├── 按功能拆分目录
├── 引入 Protocol 解耦
└── 建立 ServiceLocator

Phase 2: 组件化
├── 拆分为独立 Pod
├── 引入 Mediator/路由
└── 独立编译、独立开发

Phase 3: 插件化(可选)
├── 非核心模块动态加载
├── 预置多套业务 Bundle
└── 或引入 RN/Flutter 做动态化

八、总结

架构 适用场景 核心手段
模块化 中小型 App、团队 < 10 人 目录拆分、Protocol、依赖注入
组件化 中大型 App、多业务线并行 独立 Pod、Mediator、路由
插件化 需要动态化、热更新 动态 Framework、Bundle、JS 引擎

架构没有银弹,需结合团队规模、业务复杂度、迭代节奏选择合适方案。建议从模块化起步,随着复杂度提升再逐步演进到组件化,插件化则按实际需求谨慎引入。


参考资源

Android 模块化、组件化、插件化架构

从基础概念到实战应用,系统梳理 Android 架构演进之路


一、基础概念

1.1 为什么需要架构优化?

随着业务迭代,单体 App 会遇到诸多问题:

问题 表现 影响
代码耦合严重 模块间直接 import,循环依赖 难以维护、编译慢
编译效率低 改一行代码全量编译 开发效率下降
团队协作冲突 多人修改同一工程 Git 冲突频繁
无法独立开发 强依赖主工程 无法并行开发、独立测试
复用困难 业务逻辑与 UI 混在一起 跨项目复用成本高

1.2 三种架构模式辨析

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────────────────────────────────┐
│ 架构演进路径 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 单体架构 ──► 模块化 ──► 组件化 ──► 插件化 │
│ │ │ │ │ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 按功能拆分 Gradle模块 路由解耦 运行时动态加载 │
│ 包结构划分 build独立 ARouter等 热更新/按需下载 │
│ │
└─────────────────────────────────────────────────────────────────┘

模块化(Modularization)

  • 定义:按业务功能将代码拆分为独立的 Gradle 模块,每个模块有清晰的 build.gradle 和边界
  • 特点:物理隔离、可独立编译,通常仍在同一 Git 仓库内
  • 粒度:中等,如「用户模块」「订单模块」「支付模块」

组件化(Componentization)

  • 定义:在模块化基础上,通过路由/服务发现实现模块间完全解耦,可独立开发、独立运行
  • 特点:依赖倒置、接口隔离、可单模块调试(Application 切换)
  • 粒度:较细,如「登录组件」「分享组件」「埋点组件」

插件化(Pluginization)

  • 定义:插件 APK/Dex 可动态加载、热更新,主 App 与插件解耦到运行时
  • 特点:运行时动态、按需下载、可热修复、减小包体积
  • 粒度:独立 APK/模块

二、模块化原理与实践

2.1 模块化的核心原则

  1. 单一职责:每个模块只负责一块业务
  2. 接口隔离:模块间通过接口/路由通信,不暴露实现细节
  3. 依赖倒置:依赖抽象(interface)而非具体实现

2.2 Gradle 多模块目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MyApp/
├── app/ # 主工程壳
│ └── build.gradle
├── module_user/ # 用户模块
│ ├── build.gradle
│ └── src/main/
├── module_order/ # 订单模块
│ └── build.gradle
├── module_payment/ # 支付模块
│ └── build.gradle
├── common_base/ # 公共基础库
│ ├── network/
│ ├── utils/
│ └── base/
├── common_router/ # 路由层
│ └── build.gradle
├── build.gradle
└── settings.gradle

2.3 settings.gradle 与模块声明

1
2
3
4
5
6
7
8
// settings.gradle
rootProject.name = "MyApp"
include ':app'
include ':module_user'
include ':module_order'
include ':module_payment'
include ':common_base'
include ':common_router'

2.4 模块间依赖配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// app/build.gradle - 主工程依赖各业务模块
dependencies {
implementation project(':module_user')
implementation project(':module_order')
implementation project(':module_payment')
implementation project(':common_router')
implementation project(':common_base')
}

// module_user/build.gradle - 业务模块只依赖基础库和路由
dependencies {
implementation project(':common_base')
implementation project(':common_router')
// 不要依赖 module_order、module_payment!
}

// common_base/build.gradle - 基础库不依赖任何业务模块
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
}

2.5 模块间通信:接口 + 实现注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// common_router 中定义接口
interface IUserService {
fun getCurrentUser(): User?
fun logout()
}

// module_user 中实现接口
class UserServiceImpl : IUserService {
override fun getCurrentUser(): User? = UserManager.currentUser
override fun logout() { /* ... */ }
}

// 通过 ServiceLocator 或依赖注入框架注入
object ServiceLocator {
var userService: IUserService? = null
}

// module_order 中调用
class OrderActivity : AppCompatActivity() {
private val userService: IUserService? by lazy { ServiceLocator.userService }

fun showUserInfo() {
userService?.getCurrentUser()?.let { user ->
// 显示用户信息
}
}
}

三、组件化原理与实现

3.1 组件化架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
                ┌──────────────────┐
│ app (壳) │
│ 主工程/组装 │
└────────┬─────────┘

┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ module_user │ │ module_order│ │module_payment│
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────┼───────────────┘

┌──────▼──────┐
│ ARouter │
│ 路由中间层 │
└──────┬──────┘

┌──────▼──────┐
│ common_base │
│ 基础组件库 │
└─────────────┘

3.2 路由方案对比

方案 原理 优点 缺点
ARouter 注解 + 编译期生成路由表 阿里出品、生态成熟、支持拦截器 需引入注解处理器
WMRouter 美团方案,分组路由 按需加载、包体积优化 学习成本略高
CC (Component Call) 组件调用框架 同步/异步调用、跨进程 概念较多
手写路由表 Map 映射 简单、无依赖 维护成本高

3.3 单模块独立运行(Application 切换)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// module_user/build.gradle
android {
defaultConfig {
// 作为 Application 运行时使用
if (project.hasProperty('runAsApp') && runAsApp) {
applicationId "com.example.user"
}
}
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
if (project.hasProperty('runAsApp') && runAsApp) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- module_user/src/main/debug/AndroidManifest.xml - 独立运行时的 Manifest -->
<manifest>
<application
android:name=".UserDebugApplication"
android:label="User Module Debug">
<activity android:name=".UserActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
1
2
# 独立运行用户模块
./gradlew :module_user:installDebug -PrunAsApp=true

3.4 ARouter 使用与原理

3.4.1 添加依赖

1
2
3
4
5
// 各模块 build.gradle
dependencies {
implementation 'com.alibaba:arouter-api:1.5.2'
kapt 'com.alibaba:arouter-compiler:1.5.2'
}

3.4.2 路由配置与跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ========== 组件端:Order 模块 ==========
// 在 OrderListActivity 上添加路由注解
@Route(path = "/order/list")
class OrderListActivity : AppCompatActivity() {
@Autowired
lateinit var userId: String

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ARouter.getInstance().inject(this)
// userId 已自动注入
}
}

// ========== 调用端:任意模块 ==========
ARouter.getInstance()
.build("/order/list")
.withString("userId", "12345")
.navigation()

3.4.3 服务发现(跨模块获取实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义接口(可在 common_base 或单独 api 模块)
interface IOrderService {
fun getOrderCount(userId: String): Int
}

// Order 模块实现并注册
@Route(path = "/service/order")
class OrderServiceImpl : IOrderService {
override fun getOrderCount(userId: String): Int = /* ... */
}

// 调用端获取
val orderService = ARouter.getInstance()
.navigation(IOrderService::class.java)
orderService?.getOrderCount("12345")

3.4.5 ARouter 核心源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 简化版 ARouter 核心逻辑
// 1. 初始化时扫描 Dex,加载路由表(通过 Gradle 插件编译期生成)
class Warehouse {
// 路由表:path -> RouteMeta
static Map<String, RouteMeta> routes = ConcurrentHashMap()
// 分组缓存
static Map<String, Class<? extends IRouteGroup>> groupsIndex = ConcurrentHashMap()
}

// 2. navigation 流程
fun navigation(path: String): Postcard {
val meta = Warehouse.routes[path] // 从路由表获取
return Postcard(path, meta)
}

fun Postcard.navigation(): Any? {
return _ARouter.navigation(context, this, requestCode, navigationCallback)
}

// 3. 实际跳转
fun _ARouter.navigation(...) {
when (meta.type) {
RouteType.ACTIVITY -> {
val intent = Intent(context, meta.destination)
// 注入参数
context.startActivity(intent)
}
RouteType.ISERVICE -> {
return meta.destination.newInstance() // 服务实现类
}
}
}

// 4. 编译期注解处理器生成路由表
// 生成类似:ARouter$$Group$$order、ARouter$$Providers$$order
// 在 init() 时通过反射加载到 Warehouse

3.5 依赖关系设计原则

1
2
3
4
5
6
7
8
9
业务模块 (user/order/payment)

├── 只依赖 common_base、common_router (arouter-api)

└── 业务模块之间 不直接依赖

app 壳工程

└── 依赖所有业务模块,负责组装和 Application 初始化

四、插件化原理与实现

4.1 插件化的应用场景

  • 热更新:修复线上 Bug 无需发版
  • 按需加载:减小包体积,冷启动只加载核心
  • 动态能力:运营活动插件、A/B 测试模块
  • 多端复用:同一套插件可被主 App、Split APK 加载

4.2 Android 插件化技术选型

方案 说明 适用场景
Dynamic Feature Modules Google 官方,AGP 支持 按需下载、免安装功能模块
VirtualAPK 滴滴开源 完整的插件化框架
RePlugin 360 出品 插件即独立 APK,稳定成熟
Shadow 腾讯开源 支持 Android 10+,零 Hook
Atlas 阿里 大型 App 动态化

4.3 Dynamic Feature Modules(官方方案)

4.3.1 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// settings.gradle
include ':app'
include ':feature:order' // 动态功能模块

// app/build.gradle
dependencies {
implementation project(':common_base')
dynamicFeatures = [':feature:order']
}

// feature/order/build.gradle
apply plugin: 'com.android.dynamic-feature'
android {
defaultConfig {
minSdk 24
}
}
dependencies {
implementation project(':app')
}

4.3.2 按需下载与安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用 Play Core Library 下载动态模块
val splitInstallManager = SplitInstallManagerFactory.create(context)
val request = SplitInstallRequest.newBuilder()
.addModule("order")
.build()

splitInstallManager.startInstall(request)
.addOnSuccessListener {
// 安装成功,可跳转
startActivity(Intent(this, OrderActivity::class.java))
}
.addOnFailureListener {
// 处理失败
}

4.4 RePlugin 核心原理简述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────────────────┐
│ Host App │
│ ┌────────────────────────────────────────────────┐ │
│ │ RePluginHostConfig / PluginManager │ │
│ │ - 加载插件 APK │ │
│ │ - 替换 ClassLoader │ │
│ │ - Hook Activity/Service 等组件 │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘

┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Plugin1 │ │ Plugin2 │ │ Plugin3 │
│ .apk │ │ .apk │ │ .apk │
└──────────┘ └──────────┘ └──────────┘

核心流程

  1. Dex 加载:将插件 APK 的 classes.dex 加入宿主的 DexPathList
  2. 资源加载:创建 Resources,合并插件的 AssetManager
  3. 组件占位:通过 Hook Instrumentation/IActivityManager,用占位 Activity 代理插件 Activity

4.5 手写简易插件加载(Dex 加载)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 简化示例:加载插件 APK 中的类
class PluginLoader(private val context: Context) {

private val pluginClassLoaders = mutableMapOf<String, DexClassLoader>()

fun loadPlugin(apkPath: String): Boolean {
val optimizedDir = File(context.filesDir, "plugin_opt").apply { mkdirs() }
val libDir = File(context.filesDir, "plugin_lib").apply { mkdirs() }

val classLoader = DexClassLoader(
apkPath,
optimizedDir.absolutePath,
null,
context.classLoader
)
pluginClassLoaders[apkPath] = classLoader
return true
}

fun loadClass(apkPath: String, className: String): Class<*>? {
val loader = pluginClassLoaders[apkPath] ?: return null
return try {
loader.loadClass(className)
} catch (e: ClassNotFoundException) {
null
}
}
}

// 加载插件中的 Activity(实际还需 Hook 组件启动流程)
val loader = PluginLoader(context)
loader.loadPlugin("/sdcard/plugin.apk")
val clazz = loader.loadClass("/sdcard/plugin.apk", "com.plugin.MainActivity")
val activity = clazz.newInstance()

4.6 插件化资源加载

1
2
3
4
5
6
7
8
9
10
11
12
// 合并插件资源到宿主
fun createPluginResources(hostResources: Resources, apkPath: String): Resources {
val assets = AssetManager::class.java.newInstance()
val addAssetPath = AssetManager::class.java.getMethod("addAssetPath", String::class.java)
addAssetPath.invoke(assets, apkPath)

return Resources(
assets,
hostResources.displayMetrics,
hostResources.configuration
)
}

五、ARouter 框架源码深度解析

5.1 整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌────────────────────────────────────────────────────────┐
│ ARouter │
│ - 对外 API:build、navigation、inject │
└────────────────────────────────────────────────────────┘

┌─────────────────────────┴─────────────────────────────┐
│ _ARouter (内部实现) │
│ - LogisticsCenter:路由表、分组加载 │
│ - Warehouse:路由/服务/拦截器 存储 │
└────────────────────────────────────────────────────────┘

┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 注解处理器 │ │ 拦截器链 │ │ 降级策略 │
│ 编译期生成 │ │ 可扩展 │ │ DegradeService│
└──────────────┘ └──────────────┘ └──────────────┘

5.2 LogisticsCenter 路由表加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 初始化时通过插件扫描 Dex 中的特定类
public static void init(Context context) {
try {
// 获取所有 ARouter 生成的类:ARouter$$Root$$xxx
Set<String> routerMap = ClassUtils.getFileNameByPackageName(
context, ROUTE_ROOT_PACKAGE);
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PACKAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) Class.forName(className).getConstructor().newInstance())
.loadInto(Warehouse.groupsIndex);
}
}
// 分组按需加载,避免启动时加载全部
} catch (Exception e) {
throw new HandlerException("ARouter init logistics center exception");
}
}

5.3 拦截器机制

1
2
3
4
5
6
7
8
9
10
11
12
@Interceptor(priority = 8)
class LoginInterceptor : IInterceptor {
override fun process(postcard: Postcard, callback: InterceptorCallback) {
if (postcard.extra == NEED_LOGIN && !UserManager.isLoggedIn()) {
// 未登录,拦截并跳转登录
ARouter.getInstance().build("/user/login").navigation()
callback.onInterrupt(null)
} else {
callback.onContinue(postcard)
}
}
}

六、实际项目应用案例

6.1 某电商 App 组件化拆分

1
2
3
4
5
6
7
8
9
10
11
app (壳工程)
├── module_home # 首页
├── module_product # 商品详情
├── module_cart # 购物车
├── module_order # 订单
├── module_user # 用户中心
├── module_payment # 支付
├── module_share # 分享(可复用)
├── module_analytics # 埋点(可复用)
├── common_router # ARouter 封装
└── common_base # 网络/缓存/UI 基础库

收益

  • 编译时间:全量 6min → 单模块 1min
  • 4 个业务线可并行开发,Git 冲突减少 60%
  • ShareModule、AnalyticsModule 复用到多个 App

6.2 路由表设计实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 统一路由 Path 常量
object RouterPath {
const val ORDER_LIST = "/order/list"
const val ORDER_DETAIL = "/order/detail"
const val USER_LOGIN = "/user/login"
const val USER_PROFILE = "/user/profile"
}

// 封装便捷方法
object Router {
fun toOrderList(userId: String) {
ARouter.getInstance()
.build(RouterPath.ORDER_LIST)
.withString("userId", userId)
.navigation()
}

fun toOrderDetail(orderId: String) {
ARouter.getInstance()
.build(RouterPath.ORDER_DETAIL)
.withString("orderId", orderId)
.navigation()
}
}

6.3 解耦实践:避免循环依赖

错误示例

1
2
module_order -> module_user (获取用户信息)
module_user -> module_order (跳转订单列表)

形成循环依赖,Gradle 编译失败。

正确做法

1
2
module_order -> common_router
module_user -> common_router

需要「用户信息」时,Order 通过 ARouter.getInstance().navigation(IUserService::class.java) 获取;需要「跳转订单」时,User 通过 ARouter.getInstance().build("/order/list").navigation() 跳转。

6.4 模块化 + 插件化组合:Dynamic Feature 实践

某资讯 App 将「小说」「漫画」等非核心功能做成 Dynamic Feature,用户首次进入时提示下载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检查模块是否已安装
fun isModuleInstalled(moduleName: String): Boolean {
return SplitInstallManagerFactory.create(context)
.installedModules.contains(moduleName)
}

// 进入小说模块前检查
fun openNovelModule() {
if (isModuleInstalled("novel")) {
startActivity(Intent(this, NovelActivity::class.java))
} else {
showDownloadDialog {
installDynamicModule("novel")
}
}
}

七、最佳实践与注意事项

7.1 模块拆分原则

  • 高内聚低耦合:模块内聚度高,模块间依赖少
  • 按业务边界拆分:参考 DDD 的 Bounded Context
  • 基础组件下沉:网络、缓存、日志等抽成 common_base
  • 渐进式演进:先模块化再组件化,避免一步到位导致成本过高

7.2 常见坑与规避

问题 原因 规避
编译顺序错误 模块间隐式依赖 严格检查 build.gradle,用 ./gradlew :module_x:dependencies 检查
路由表膨胀 所有页面都注册路由 仅对外暴露的页面注册,内部页面用 startActivity
启动变慢 初始化时加载全部路由 使用分组按需加载(ARouter 已支持)
R 文件冲突 多模块资源 ID 冲突 在 build.gradle 中设置 resourcePrefix "module_user_"
插件化兼容性 不同厂商 ROM 限制 优先使用 Dynamic Feature,第三方框架需充分测试

7.3 R 文件与资源隔离

1
2
3
4
// 各业务模块 build.gradle 中
android {
resourcePrefix "user_" // 资源必须以 user_ 开头
}

7.4 架构演进路线图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Phase 1: 模块化
├── 拆分为 Gradle 多模块
├── 建立 common_base、common_router
└── 模块间通过接口通信

Phase 2: 组件化
├── 引入 ARouter 路由
├── 服务发现替代直接依赖
└── 支持单模块独立运行调试

Phase 3: 插件化(可选)
├── 非核心功能改为 Dynamic Feature
├── 或引入 RePlugin/Shadow 做完整插件化
└── 按需下载、热更新

八、总结

架构 适用场景 核心手段
模块化 中小型 App、团队 < 10 人 Gradle 多模块、接口、依赖配置
组件化 中大型 App、多业务线并行 ARouter、服务发现、单模块调试
插件化 需要动态化、热更新、包体积优化 Dynamic Feature、RePlugin、Shadow

架构没有银弹,需结合团队规模、业务复杂度、迭代节奏选择合适方案。建议从模块化起步,随着复杂度提升再逐步演进到组件化,插件化则按实际需求谨慎引入。


参考资源

设计模式全解:原理、UML 与项目实战

系统梳理 GoF 23 种设计模式,结合前端与 Node.js 实战代码,配 UML 类图与时序示意图辅助理解。


目录

  1. 什么是设计模式
  2. 创建型模式(5 种)
  3. 结构型模式(7 种)
  4. 行为型模式(11 种)
  5. 速查总表

一、什么是设计模式

设计模式(Design Pattern) 是软件工程中反复出现问题的通用可复用解决方案。1994 年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides(即「四人组 GoF」)在《设计模式:可复用面向对象软件的基础》中总结了 23 种经典模式,按目的分为三类:

1
2
3
创建型(Creational)  ——  解决对象的创建问题
结构型(Structural) —— 解决类/对象的组合问题
行为型(Behavioral) —— 解决对象间职责与通信问题

设计原则(SOLID)

原则 说明
S ingle Responsibility 一个类只做一件事
O pen/Closed 对扩展开放,对修改关闭
L iskov Substitution 子类可替换父类而不破坏程序
I nterface Segregation 接口最小化,不强迫实现不需要的方法
D ependency Inversion 依赖抽象,而非具体实现

二、创建型模式(5 种)

2.1 单例模式(Singleton)

意图:保证一个类只有一个实例,并提供全局访问点。

场景:全局状态管理(Store)、日志记录器、配置管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Store {
static #instance = null;

#state = {};

static getInstance() {
if (!Store.#instance) {
Store.#instance = new Store();
}
return Store.#instance;
}

getState() { return this.#state; }
setState(patch) { Object.assign(this.#state, patch); }
}

const a = Store.getInstance();
const b = Store.getInstance();
console.log(a === b); // true

2.2 工厂方法模式(Factory Method)

意图:定义创建对象的接口,让子类决定实例化哪个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Button {
render() { throw new Error('abstract'); }
}
class PrimaryButton extends Button {
render() { return `<button class="btn-primary">OK</button>`; }
}
class DangerButton extends Button {
render() { return `<button class="btn-danger">Delete</button>`; }
}

function createButton(type) {
const map = { primary: PrimaryButton, danger: DangerButton };
const Ctor = map[type];
if (!Ctor) throw new Error(`Unknown type: ${type}`);
return new Ctor();
}

createButton('primary').render(); // <button class="btn-primary">OK</button>

2.3 抽象工厂模式(Abstract Factory)

意图:提供一个接口,用于创建一系列相关或依赖对象,而不指定具体类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const lightFactory = {
createButton: () => ({ type: 'button', theme: 'light' }),
createInput: () => ({ type: 'input', theme: 'light' }),
};
const darkFactory = {
createButton: () => ({ type: 'button', theme: 'dark' }),
createInput: () => ({ type: 'input', theme: 'dark' }),
};

function buildUI(factory) {
return {
button: factory.createButton(),
input: factory.createInput(),
};
}
buildUI(darkFactory); // { button: {theme:'dark'}, input: {theme:'dark'} }

2.4 建造者模式(Builder)

意图:将复杂对象的构造表示分离,同一构建过程可创建不同表示。

场景:SQL 查询构建、配置对象构造、链式调用 API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class QueryBuilder {
#table = '';
#conditions = [];
#fields = ['*'];
#limit = null;

from(table) { this.#table = table; return this; }
select(...f) { this.#fields = f; return this; }
where(cond) { this.#conditions.push(cond); return this; }
limit(n) { this.#limit = n; return this; }

build() {
let sql = `SELECT ${this.#fields.join(', ')} FROM ${this.#table}`;
if (this.#conditions.length) sql += ` WHERE ${this.#conditions.join(' AND ')}`;
if (this.#limit !== null) sql += ` LIMIT ${this.#limit}`;
return sql;
}
}

new QueryBuilder()
.from('users')
.select('id', 'name')
.where('age > 18')
.where('active = 1')
.limit(10)
.build();
// SELECT id, name FROM users WHERE age > 18 AND active = 1 LIMIT 10

2.5 原型模式(Prototype)

意图:通过复制(克隆)已有实例来创建新对象,而不是通过 new

1
2
3
4
5
6
7
8
9
10
11
12
13
const userProto = {
greet() { return `Hi, I'm ${this.name}`; },
clone() { return Object.create(this); },
};

const alice = Object.create(userProto);
alice.name = 'Alice';

const bob = alice.clone();
bob.name = 'Bob';

alice.greet(); // Hi, I'm Alice
bob.greet(); // Hi, I'm Bob

三、结构型模式(7 种)

3.1 适配器模式(Adapter)

意图:将一个接口转换成调用方期望的另一个接口——解决不兼容问题。

场景:旧接口兼容、第三方 SDK 包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 旧日志库,接口为 log(msg)
class OldLogger {
log(msg) { console.log('[OLD]', msg); }
}

// 新系统要求 info / warn / error 三个方法
class LoggerAdapter {
constructor(logger) { this.logger = logger; }
info(msg) { this.logger.log(`INFO ${msg}`); }
warn(msg) { this.logger.log(`WARN ${msg}`); }
error(msg) { this.logger.log(`ERROR ${msg}`); }
}

const logger = new LoggerAdapter(new OldLogger());
logger.info('server started'); // [OLD] INFO server started

3.2 桥接模式(Bridge)

意图:将抽象部分与实现部分分离,使二者可以独立变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 实现部分:渲染引擎
const canvasRenderer = { draw: (shape) => `Canvas: draw ${shape}` };
const svgRenderer = { draw: (shape) => `SVG: draw ${shape}` };

// 抽象部分:形状
class Shape {
constructor(renderer) { this.renderer = renderer; }
}
class Circle extends Shape {
draw() { return this.renderer.draw('circle'); }
}
class Square extends Shape {
draw() { return this.renderer.draw('square'); }
}

new Circle(svgRenderer).draw(); // SVG: draw circle
new Square(canvasRenderer).draw(); // Canvas: draw square

3.3 组合模式(Composite)

意图:将对象组织成树形结构,使单个对象和组合对象的使用方式一致。

场景:文件系统、组件树、权限树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class File {
constructor(name, size) { this.name = name; this.size = size; }
getSize() { return this.size; }
}
class Folder {
constructor(name) { this.name = name; this.children = []; }
add(child) { this.children.push(child); return this; }
getSize() { return this.children.reduce((s, c) => s + c.getSize(), 0); }
}

const root = new Folder('root')
.add(new File('a.js', 10))
.add(
new Folder('src')
.add(new File('index.js', 20))
.add(new File('utils.js', 15))
);

root.getSize(); // 45

3.4 装饰器模式(Decorator)

意图:在不改变原对象的前提下,动态地为其添加功能。

场景:中间件(Koa/Express)、日志增强、缓存包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function withLogging(fn) {
return function (...args) {
console.log(`[CALL] ${fn.name}(${args})`);
const result = fn.apply(this, args);
console.log(`[RET] ${result}`);
return result;
};
}

function add(a, b) { return a + b; }
const loggedAdd = withLogging(add);
loggedAdd(2, 3);
// [CALL] add(2,3)
// [RET] 5

3.5 外观模式(Facade)

意图:为子系统提供统一的高层接口,隐藏内部复杂性。

场景:SDK 封装、初始化流程统一入口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AuthService   { login(u, p)    { return { token: 'tok_' + u }; } }
class ProfileService { fetch(token) { return { name: 'Alice' }; } }
class LogService { record(action) { console.log('[LOG]', action); } }

class AppFacade {
#auth = new AuthService();
#profile = new ProfileService();
#log = new LogService();

async signIn(username, password) {
const { token } = this.#auth.login(username, password);
const profile = this.#profile.fetch(token);
this.#log.record(`login:${username}`);
return { token, profile };
}
}

new AppFacade().signIn('alice', 'pw');

3.6 享元模式(Flyweight)

意图:共享细粒度对象以节省内存,将对象内在状态与外在状态分离。

场景:字符渲染、粒子系统、大量相似节点池。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class CharStyle {
constructor(font, size, color) {
this.font = font; this.size = size; this.color = color;
}
}

class CharStylePool {
#pool = new Map();
get(font, size, color) {
const key = `${font}-${size}-${color}`;
if (!this.#pool.has(key)) this.#pool.set(key, new CharStyle(font, size, color));
return this.#pool.get(key);
}
size() { return this.#pool.size; }
}

const pool = new CharStylePool();
// 渲染 1000 个字符,只需要极少的 CharStyle 对象
const chars = Array.from('Hello World x1000').map(ch => ({
char: ch,
style: pool.get('Arial', 14, '#333'),
}));
console.log(pool.size()); // 1(所有字符共享同一样式对象)

3.7 代理模式(Proxy)

意图:为另一个对象提供代理以控制对它的访问。

场景:懒加载、权限控制、缓存、数据校验(ES Proxy)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function createValidator(target, rules) {
return new Proxy(target, {
set(obj, prop, value) {
if (rules[prop] && !rules[prop](value)) {
throw new TypeError(`Invalid value for "${prop}": ${value}`);
}
obj[prop] = value;
return true;
},
});
}

const user = createValidator({}, {
age: v => typeof v === 'number' && v >= 0 && v <= 150,
email: v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
});

user.age = 25; // OK
user.email = 'a@b.com'; // OK
// user.age = -1; // TypeError: Invalid value for "age": -1

四、行为型模式(11 种)

4.1 责任链模式(Chain of Responsibility)

意图:将请求沿着处理者链传递,直到某个处理者处理它为止。

场景:中间件管道(Koa)、审批流程、事件冒泡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Handler {
setNext(handler) { this.next = handler; return handler; }
handle(req) { return this.next?.handle(req); }
}

class AuthHandler extends Handler {
handle(req) {
if (!req.token) return '401 Unauthorized';
return super.handle(req);
}
}
class RoleHandler extends Handler {
handle(req) {
if (req.role !== 'admin') return '403 Forbidden';
return super.handle(req);
}
}
class BusinessHandler extends Handler {
handle(req) { return `200 OK: Hello ${req.user}`; }
}

const chain = new AuthHandler();
chain.setNext(new RoleHandler()).setNext(new BusinessHandler());

chain.handle({ token: 'tok', role: 'admin', user: 'Alice' }); // 200 OK
chain.handle({ token: 'tok', role: 'guest' }); // 403 Forbidden
chain.handle({}); // 401 Unauthorized

4.2 命令模式(Command)

意图:将操作封装为对象,支持撤销、重做、队列调度。

场景:编辑器操作历史、任务队列、宏录制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class TextEditor {
#text = '';
#history = [];

execute(command) {
this.#text = command.execute(this.#text);
this.#history.push(command);
return this;
}
undo() {
const cmd = this.#history.pop();
if (cmd) this.#text = cmd.undo(this.#text);
return this;
}
value() { return this.#text; }
}

const appendCmd = (str) => ({
execute: (t) => t + str,
undo: (t) => t.slice(0, -str.length),
});

const ed = new TextEditor();
ed.execute(appendCmd('Hello')).execute(appendCmd(', World'));
ed.value(); // Hello, World
ed.undo();
ed.value(); // Hello

4.3 解释器模式(Interpreter)

意图:为语言定义文法,并实现解释器来解释该语言的句子。

场景:模板引擎、DSL 解析、规则引擎。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 极简四则运算解释器
function interpret(expr) {
expr = expr.trim();
const addIdx = expr.lastIndexOf('+');
const subIdx = expr.lastIndexOf('-');
const pivot = Math.max(addIdx, subIdx);
if (pivot > 0) {
const left = interpret(expr.slice(0, pivot));
const right = interpret(expr.slice(pivot + 1));
return expr[pivot] === '+' ? left + right : left - right;
}
return parseFloat(expr);
}

interpret('1 + 2 + 3'); // 6
interpret('10 - 3 + 2'); // 9

4.4 迭代器模式(Iterator)

意图:提供顺序访问集合元素的方法,而不暴露其内部表示。

JavaScript 内置迭代器协议(Symbol.iterator)即此模式的语言级实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Range {
constructor(start, end, step = 1) {
this.start = start; this.end = end; this.step = step;
}
[Symbol.iterator]() {
let cur = this.start;
return {
next: () => cur <= this.end
? { value: cur += this.step, done: false }
: { done: true },
};
}
}

[...new Range(1, 10, 2)]; // [3, 5, 7, 9, 11] — 每步 +2
for (const n of new Range(0, 5)) console.log(n); // 1 2 3 4 5

4.5 中介者模式(Mediator)

意图:用中介对象封装一组对象的交互,减少对象间直接引用。

场景:聊天室、事件总线、机场调度。

1
2
3
4
5
6
7
8
9
10
11
12
13
class EventBus {
#listeners = new Map();
on(event, fn) { (this.#listeners.get(event) ?? this.#listeners.set(event, new Set()).get(event)).add(fn); }
off(event, fn) { this.#listeners.get(event)?.delete(fn); }
emit(event, data) { this.#listeners.get(event)?.forEach(fn => fn(data)); }
}

const bus = new EventBus();
bus.on('login', ({ user }) => console.log(`Welcome, ${user}`));
bus.on('login', ({ user }) => console.log(`Log: ${user} logged in`));
bus.emit('login', { user: 'Alice' });
// Welcome, Alice
// Log: Alice logged in

4.6 备忘录模式(Memento)

意图:在不破坏封装的前提下,捕获并外部化对象的内部状态,以便恢复。

场景:撤销/重做、草稿保存、游戏存档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class FormState {
#snapshots = [];
#current = {};

set(patch) { this.#current = { ...this.#current, ...patch }; }
snapshot() { this.#snapshots.push({ ...this.#current }); }
restore() { if (this.#snapshots.length) this.#current = this.#snapshots.pop(); }
get() { return { ...this.#current }; }
}

const form = new FormState();
form.set({ name: 'Alice' });
form.snapshot();
form.set({ name: 'Bob', age: 30 });
form.get(); // { name: 'Bob', age: 30 }
form.restore();
form.get(); // { name: 'Alice' }

4.7 观察者模式(Observer)

意图:对象间一对多依赖,当一个对象状态变化时,自动通知所有依赖对象。

场景:Vue/React 响应式、DOM 事件、数据绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Subject {
#observers = new Set();
#state;

subscribe(fn) { this.#observers.add(fn); }
unsubscribe(fn) { this.#observers.delete(fn); }

setState(val) {
this.#state = val;
this.#observers.forEach(fn => fn(val));
}
getState() { return this.#state; }
}

const counter = new Subject();
counter.subscribe(v => console.log('A sees:', v));
counter.subscribe(v => console.log('B sees:', v));
counter.setState(42);
// A sees: 42
// B sees: 42

4.8 状态模式(State)

意图:允许对象在内部状态改变时改变其行为,使对象看起来修改了它的类。

场景:订单状态机、红绿灯、播放器状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const states = {
idle: { fetch: (ctx) => ctx.transition('loading') },
loading: {
resolve: (ctx, data) => { ctx.data = data; ctx.transition('success'); },
reject: (ctx, err) => { ctx.error = err; ctx.transition('error'); },
},
success: { reset: (ctx) => ctx.transition('idle') },
error: { reset: (ctx) => ctx.transition('idle') },
};

class AsyncMachine {
#state = 'idle';
data = null;
error = null;

transition(s) { this.#state = s; console.log('→', s); }
dispatch(action, payload) {
const handler = states[this.#state]?.[action];
if (!handler) return console.warn(`No "${action}" in state "${this.#state}"`);
handler(this, payload);
}
getState() { return this.#state; }
}

const m = new AsyncMachine();
m.dispatch('fetch'); // → loading
m.dispatch('resolve', { id: 1 }); // → success
m.dispatch('reset'); // → idle

4.9 策略模式(Strategy)

意图:定义一系列算法,封装每一个并使它们可互换,独立于使用者而变化。

场景:排序算法、支付方式、表单验证、折扣计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const sortStrategies = {
bubble: (arr) => {
const a = [...arr];
for (let i = 0; i < a.length; i++)
for (let j = 0; j < a.length - i - 1; j++)
if (a[j] > a[j+1]) [a[j], a[j+1]] = [a[j+1], a[j]];
return a;
},
quick: (arr) => {
if (arr.length <= 1) return arr;
const pivot = arr[0];
const left = arr.slice(1).filter(x => x <= pivot);
const right = arr.slice(1).filter(x => x > pivot);
return [...sortStrategies.quick(left), pivot, ...sortStrategies.quick(right)];
},
};

class Sorter {
constructor(strategy = 'quick') { this.strategy = strategy; }
sort(arr) { return sortStrategies[this.strategy](arr); }
}

new Sorter('bubble').sort([3, 1, 4, 1, 5]); // [1, 1, 3, 4, 5]
new Sorter('quick').sort([3, 1, 4, 1, 5]); // [1, 1, 3, 4, 5]

4.10 模板方法模式(Template Method)

意图:在基类定义算法骨架,将某些步骤的实现延迟到子类,不改变结构只改变细节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class DataExporter {
// 模板方法:算法骨架
export(data) {
const processed = this.process(data);
const formatted = this.format(processed);
this.save(formatted);
}

process(data) { return data.filter(Boolean); } // 默认实现
format(data) { throw new Error('abstract'); } // 子类实现
save(content) { console.log('saving:', content.slice(0, 40)); }
}

class CSVExporter extends DataExporter {
format(data) { return data.map(r => Object.values(r).join(',')).join('\n'); }
}
class JSONExporter extends DataExporter {
format(data) { return JSON.stringify(data, null, 2); }
}

const rows = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
new CSVExporter().export(rows); // saving: 1,Alice\n2,Bob
new JSONExporter().export(rows); // saving: [\n {\n "id": 1,

4.11 访问者模式(Visitor)

意图:在不改变元素类的前提下,为其定义作用于这些元素的新操作。

场景:AST 遍历、报表统计、编译器优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class NumberNode  { constructor(v)    { this.value = v; } accept(v) { return v.visitNumber(this); } }
class AddNode { constructor(l, r) { this.left = l; this.right = r; } accept(v) { return v.visitAdd(this); } }
class MulNode { constructor(l, r) { this.left = l; this.right = r; } accept(v) { return v.visitMul(this); } }

// 求值访问者
const evalVisitor = {
visitNumber: (n) => n.value,
visitAdd: (n) => n.left.accept(evalVisitor) + n.right.accept(evalVisitor),
visitMul: (n) => n.left.accept(evalVisitor) * n.right.accept(evalVisitor),
};

// 打印访问者
const printVisitor = {
visitNumber: (n) => `${n.value}`,
visitAdd: (n) => `(${n.left.accept(printVisitor)} + ${n.right.accept(printVisitor)})`,
visitMul: (n) => `(${n.left.accept(printVisitor)} * ${n.right.accept(printVisitor)})`,
};

// (2 + 3) * 4
const ast = new MulNode(new AddNode(new NumberNode(2), new NumberNode(3)), new NumberNode(4));
ast.accept(evalVisitor); // 20
ast.accept(printVisitor); // ((2 + 3) * 4)

五、速查总表

分类 模式 核心意图 常见场景
创建型 单例 唯一实例 Store、Logger、Config
创建型 工厂方法 子类决定实例化 组件工厂、解析器
创建型 抽象工厂 相关对象族 主题 UI、跨平台组件
创建型 建造者 分步构建复杂对象 QueryBuilder、配置
创建型 原型 克隆已有实例 对象池、深拷贝
结构型 适配器 接口转换 旧库兼容、SDK 包装
结构型 桥接 抽象与实现分离 渲染引擎、驱动层
结构型 组合 树形统一接口 文件系统、组件树
结构型 装饰器 动态扩展功能 中间件、HOC、缓存
结构型 外观 统一高层接口 SDK 初始化入口
结构型 享元 共享细粒度对象 字符渲染、粒子池
结构型 代理 控制对象访问 懒加载、权限、校验
行为型 责任链 链式处理请求 中间件管道、审批流
行为型 命令 操作对象化 撤销/重做、任务队列
行为型 解释器 语言文法解析 模板引擎、DSL
行为型 迭代器 统一遍历接口 for…of、Generator
行为型 中介者 集中对象交互 EventBus、聊天室
行为型 备忘录 状态快照/恢复 撤销历史、草稿
行为型 观察者 自动通知订阅者 Vue 响应式、事件
行为型 状态 状态驱动行为 订单流、状态机
行为型 策略 算法可互换 支付、排序、折扣
行为型 模板方法 固定骨架/可变步骤 导出流程、生命周期
行为型 访问者 不改类增加操作 AST 遍历、报表