📌 前言
在开发 3D 场景编辑器时,我们需要管理各种驱动行为(如位置插值、旋转插值、缩放插值等)。最初使用 switch-case 来创建不同类型的行为,但随着行为类型增多,代码变得难以维护。后来我们使用自注册工厂模式重构这个系统。
🔴 问题:传统 Switch-Case 方式的困境
原始代码
TypeScript
// ❌ 硬编码的 switch-case 方式
function createBehavior(behaviorName: string): DriveBehavior | null {
switch(behaviorName) {
case "DriveLerpPositionEditor":
return new DriveLerpPositionEditor();
case "DriveLerpRotationEditor":
return new DriveLerpRotationEditor();
case "DriveLerpScalingEditor":
return new DriveLerpScalingEditor();
case "DriveLerpVisibilityEditor":
return new DriveLerpVisibilityEditor();
case "DriveAxisRotateEditor":
return new DriveAxisRotateEditor();
// 每次新增行为都要修改这里...
default:
console.warn(`Unknown behavior: ${behaviorName}`);
return null;
}
}
存在的问题
- 违反开闭原则:每次添加新行为都要修改工厂函数
- 代码重复:大量的 case 语句重复相似的模式
- 容易出错:忘记添加 case 会导致行为无法创建
- 难以维护:行为类型增多时,switch 语句会变得冗长
💡 解决方案:自注册工厂模式
核心思想
让每个行为类自己注册到工厂中,而不是在工厂中硬编码。
架构设计

🛠️ 实现步骤
步骤 1:定义行为类接口
TypeScript
// DriveBehaviorEditor.ts
export default class DriveBehaviorEditor implements Behavior<TransformNode> {
// 基础行为类...
getJson(): string { return ''; }
setByJson(json: string): void { }
}
// 行为类的静态接口
export interface DriveBehaviorEditorClass {
// 行为的唯一标识名称
readonly behaviorName: string;
// 创建行为实例的工厂方法
createInstance(): DriveBehaviorEditor;
}
步骤 2:创建注册表
TypeScript
// BehaviorRegistryEditor.ts
export class BehaviorRegistryEditor {
// ✅ 所有可用的行为类列表(只需在这里添加)
private static behaviorClasses: DriveBehaviorEditorClass[] = [
DriveLerpPositionEditor as any,
DriveLerpRotationEditor as any,
DriveLerpScalingEditor as any,
DriveLerpVisibilityEditor as any,
DriveAxisRotateEditor as any,
// 新增行为?只需在这里加一行!
];
// 行为名称到行为类的映射表
private static registry = new Map<string, DriveBehaviorEditorClass>();
// 自动初始化注册表
public static initialize(): void {
if (this.initialized) return;
this.behaviorClasses.forEach(behaviorClass => {
const behaviorName = behaviorClass.behaviorName;
if (!behaviorName) {
console.warn(`Behavior class missing behaviorName`);
return;
}
this.registry.set(behaviorName, behaviorClass);
});
this.initialized = true;
}
// ✅ 根据名称创建行为实例(核心方法)
public static createBehavior(behaviorName: string): DriveBehaviorEditor | null {
if (!this.initialized) {
this.initialize();
}
const behaviorClass = this.registry.get(behaviorName);
if (!behaviorClass) {
console.warn(`Unknown behavior type: ${behaviorName}`);
return null;
}
return behaviorClass.createInstance();
}
}
// 自动初始化
BehaviorRegistryEditor.initialize();
步骤 3:让行为类实现自注册接口
TypeScript
// DriveLerpPositionEditor.ts
export default class DriveLerpPositionEditor extends DriveLerpTranEditor {
// ✅ 静态属性:行为名称
public static readonly behaviorName = "DriveLerpPositionEditor";
// ✅ 静态方法:创建实例
public static createInstance(): DriveLerpPositionEditor {
return new DriveLerpPositionEditor();
}
// ... 其他实现
}
步骤 4:在管理器中使用
TypeScript
// DriveBehaviorManagerEditor.ts
export default class DriveBehaviorManagerEditor {
public addDriveBehavior(target: TransformNode, behaviorName: string) {
// ✅ 简洁的调用方式
const behavior = BehaviorRegistryEditor.createBehavior(behaviorName);
if (!behavior) {
console.warn(`Failed to create behavior: ${behaviorName}`);
return null;
}
// 添加到节点...
nodeDriveBehavManager.addDriveBehavior(behavior);
return behavior;
}
}
📊 对比:Before vs After
Before(Switch-Case)❌
TypeScript
// 每次新增行为需要修改这里
function createBehavior(name: string) {
switch(name) {
case "DriveLerpPositionEditor":
return new DriveLerpPositionEditor();
case "DriveLerpRotationEditor":
return new DriveLerpRotationEditor();
// ... 更多 case
}
}
// 使用
const behavior = createBehavior("DriveLerpPositionEditor");
After(自注册工厂)✅
TypeScript
// 新增行为只需在行为类列表中添加
private static behaviorClasses = [
DriveLerpPositionEditor,
DriveLerpRotationEditor,
NewAwesomeBehavior, // ← 新增:只需加一行
];
// 使用(完全相同)
const behavior = BehaviorRegistryEditor.createBehavior("DriveLerpPositionEditor");
🎯 优势总结
| 维度 | Switch-Case | 自注册工厂 |
|---|---|---|
| 扩展性 | ❌ 每次修改工厂代码 | ✅ 只需添加到列表 |
| 维护性 | ❌ 代码冗长重复 | ✅ 代码简洁清晰 |
| 开闭原则 | ❌ 不符合 | ✅ 符合 |
| 类型安全 | ⚠️ 依赖字符串 | ✅ TypeScript 检查 |
| 可测试性 | ⚠️ 难以 mock | ✅ 易于测试 |
具体优势
1. 易于扩展
TypeScript
// 新增行为:3步完成
// 1. 创建新行为类
class NewBehaviorEditor extends DriveBehaviorEditor {
static readonly behaviorName = "NewBehaviorEditor";
static createInstance() { return new NewBehaviorEditor(); }
}
// 2. 注册到列表(仅一行)
private static behaviorClasses = [
// ... 现有行为
NewBehaviorEditor, // ← 新增
];
// 3. 完成!可以使用了
const behavior = BehaviorRegistryEditor.createBehavior("NewBehaviorEditor");
2. 类型安全
TypeScript
// TypeScript 会检查每个行为类是否实现了接口
class InvalidBehavior {
// ❌ 缺少 behaviorName 或 createInstance
// TypeScript 会报错
}
3. 集中管理
TypeScript
// 获取所有已注册的行为
const allBehaviors = BehaviorRegistryEditor.getRegisteredBehaviors();
console.log(allBehaviors); // ["DriveLerpPositionEditor", "DriveLerpRotationEditor", ...]
// 检查某个行为是否已注册
const isRegistered = BehaviorRegistryEditor.isRegistered("DriveLerpPositionEditor");
🔄 完整的数据流
保存场景(序列化)
TypeScript
// 1. 获取行为数据
const json = behavior.getJson();
// 2. 创建 DTO
const behaviorInfo = new DTO_BehaviorInfo(behavior.name, json);
// 3. 保存到文件
saveToFile(behaviorInfo);
加载场景(反序列化)
TypeScript
// 1. 从文件读取
const behaviorInfo = loadFromFile();
// 2. 使用工厂创建实例
const behavior = BehaviorRegistryEditor.createBehavior(behaviorInfo.type);
// 3. 恢复状态
behavior.setByJson(behaviorInfo.json);
// 4. 添加到场景
nodeDriveBehavManager.addDriveBehavior(behavior);
💻 完整示例
创建新行为类
TypeScript
// DriveNewAwesomeEditor.ts
export default class DriveNewAwesomeEditor extends DriveBehaviorEditor {
// ✅ 必需:行为名称
public static readonly behaviorName = "DriveNewAwesomeEditor";
// ✅ 必需:工厂方法
public static createInstance(): DriveNewAwesomeEditor {
return new DriveNewAwesomeEditor();
}
// 实现特定逻辑
protected _updatePreview(): void {
// 你的逻辑...
}
// 实现序列化
public override getJson(): string {
return JSON.stringify({ /* ... */ });
}
// 实现反序列化
public override setByJson(json: string): void {
const data = JSON.parse(json);
// 恢复状态...
}
}
注册新行为
TypeScript
// BehaviorRegistryEditor.ts
private static behaviorClasses = [
DriveLerpPositionEditor,
DriveLerpRotationEditor,
DriveLerpScalingEditor,
DriveLerpVisibilityEditor,
DriveAxisRotateEditor,
DriveNewAwesomeEditor, // ← 只需加这一行!
];
使用新行为
TypeScript
// 自动可用,无需修改其他代码
const behavior = BehaviorRegistryEditor.createBehavior("DriveNewAwesomeEditor");
📈 性能影响
- 初始化:O(n) - 只在启动时执行一次
- 查找:O(1) - 使用 Map 进行查找
- 内存:可忽略 - 只存储行为类的引用
🎓 设计模式总结
这个实现结合了多个设计模式:
- 工厂模式:创建对象的统一接口
- 注册表模式:动态管理对象类型
- 策略模式:不同行为的可替换实现
- 开闭原则:对扩展开放,对修改关闭
🔚 结语
从 Switch-Case 到自注册工厂的重构,不仅仅是代码优化,更是架构思维的提升。这个模式让系统:
- ✅ 更易扩展:添加新功能只需一行代码
- ✅ 更易维护:代码结构清晰,职责分明
- ✅ 更加健壮:类型安全,不易出错
- ✅ 更易测试:每个部分都可独立测试
如果你的项目中有类似的 switch-case 工厂代码,不妨考虑使用自注册工厂模式进行重构!