一. MVC 能解决的问题
| 维度 | 当前问题 | MVC 改进 |
|---|---|---|
| 代码组织 | UI和逻辑混在一个类 | View负责UI操作,Controller负责逻辑 |
| 可读性 | 一个类300+行,混杂各种逻辑 | 分离后各自职责清晰 |
| 维护性 | 改UI要找半天逻辑代码 | 改动UI不影响逻辑 |
二. MVC 的局限性
MVC 主要解决 "UI和逻辑耦合" 的问题,但对于 "多模式分支判断" 的问题,单纯用MVC改善有限。
例如:在游戏开发中,UI 界面经常需要处理多种游戏模式的逻辑。以游戏结束界面为例:
- 经典模式:有好友分享、皮肤解锁、金币奖励等完整逻辑
- 挑战模式:无分享、无皮肤解锁、无金币奖励的结算流程
如果使用传统的 if (isMode === ModeA) { ... } else { ... } 模式判断,会导致:
- 代码逻辑分散在各处,难以维护
- 新增模式时需要修改所有判断位置
- 测试困难,难以单独验证某模式的逻辑
三. 解决方案
采用 MVC + 策略模式 的组合来解决多模式 UI 逻辑问题。
3.1 设计思路
┌───────────────────────────────────────────┐
│ View │
│ (OverLayer) - 纯 UI 渲染,不包含业务逻辑 │
└───────────────────────────────────────────┘
▲
│ 调用
│
┌───────────────────────────────────────────┐
│ Controller │
│ (OverController) - 流程编排,协调 View 和 Strategy │
└───────────────────────────────────────────┘
▲
│ 调用
│
┌───────────────────────────────────────────┐
│ Strategy │
│ (IOverStrategy) - 接口定义 │
│ (OverBaseStrategy) - 基类,通用逻辑 │
│ (OverClassicStrategy / OverChallStrategy) - 各模式实现 │
└───────────────────────────────────────────┘
3.2 职责划分
View(视图层)
职责:
- UI 组件的显示/隐藏、状态更新
- 接收用户事件,传递给 Controller
- 不包含任何业务逻辑判断
特点:
- 实现预定义的视图接口(IOverView)
- 通过 Controller 间接获取数据
- 被动等待被调用
Controller(控制器)
职责:
- 流程编排,确定调用顺序
- 获取全局数据(如排行榜名称)
- 控制 View 的加载状态
- 不包含具体业务逻辑
特点:
- 持有 View 和 Strategy 的引用
- 协调两者之间的配合
- 适合放置"显示 loading → 调用策略 → 显示结果"这类编排逻辑
Strategy(策略层)
职责:
- 所有业务逻辑判断(模式差异的核心)
- 调用 View 的时机控制
- 模式特有的数据处理
特点:
- 实现统一接口,保证一致性
- 通过依赖注入获得 View 引用
- 可以直接调用 View 的方法
// 策略接口示例
interface IOverStrategy {
readonly mode: EGameMode;
handleOverGold(score: number): void;
// ...
}
// 基类实现通用逻辑
abstract class OverBaseStrategy implements IOverStrategy {
protected _view: IOverView | null = null;
// ...
}
// 经典模式特有处理
class OverClassicStrategy extends OverBaseStrategy {
handleOverGold(score: number): void {
// ...
}
}
// 模式挑战特有处理
class OverChallStrategy extends OverBaseStrategy {
handleOverGold(score: number): void {
// 挑战模式不显示金币弹窗
}
}
3.3 模块架构
game/
├── controller/
│ └── OverController.ts # 控制器类
├── strategy/
│ ├── IOverStrategy.ts # 策略接口
│ ├── OverBaseStrategy.ts # 策略基类(通用逻辑)
│ ├── OverClassicStrategy.ts # 经典模式策略
│ └── OverChallStrategy.ts # 挑战模式策略
├── ui/
│ └── OverLayer.ts # 视图类(实现 IOverView)
└── factory/
└── OverStrategyFactory.ts # 策略工厂(可选)
3.4 适用场景
- UI 界面需要支持多种游戏模式
- 不同模式有相似的流程但具体实现不同
- 预期会有新的模式扩展
- 需要方便单独测试和修改某模式的逻辑
3.5 不适用场景
- 简单页面,逻辑简单且不会扩展
- 单一模式的页面,直接写在一起反而更清晰
3.6 注意事项
1. Strategy 依赖 View
Strategy 需要调用 View 方法,因此会依赖 View 接口。这在 Strategy 模式中是正常的 —— 策略需要知道如何展示结果。
2. 避免循环依赖
View → Controller → Strategy → View
为避免循环依赖:
- View 通过接口与 Strategy 交互
- Controller 负责初始化和协调
- 不要在 Strategy 中直接创建 View 实例
3. 接口的演进
随着需求变化,策略接口可能需要新增方法。注意:
- 新增方法时,所有实现类都需要实现
- 可以考虑使用默认参数或空实现来减少实现类的工作量
4. 工厂模式的使用
当策略创建逻辑复杂时,可以使用工厂类:
class OverStrategyFactory {
static create(mode: EGameMode): IOverStrategy {
switch (mode) {
case EGameMode.Classic:
return new OverClassicStrategy();
case EGameMode.Chall:
return new OverChallStrategy();
default:
throw new Error(`Unknown mode: ${mode}`);
}
}
}
四 总结
MVC + 策略模式的组合适用于:
- ✅ 多模式 UI 需要差异化处理
- ✅ 需要方便扩展新模式
- ✅ 业务逻辑需要与 UI 渲染分离
核心原则:
- View 只负责 UI 渲染
- Controller 只负责流程编排
- Strategy 包含所有业务逻辑,并决定何时调用 View