从 Switch-Case 到自注册工厂:优雅的驱动行为管理系统重构

📌 前言

在开发 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 进行查找
  • 内存:可忽略 - 只存储行为类的引用

🎓 设计模式总结

这个实现结合了多个设计模式:

  1. 工厂模式:创建对象的统一接口
  1. 注册表模式:动态管理对象类型
  1. 策略模式:不同行为的可替换实现
  1. 开闭原则:对扩展开放,对修改关闭

🔚 结语

从 Switch-Case 到自注册工厂的重构,不仅仅是代码优化,更是架构思维的提升。这个模式让系统:

  • ✅ 更易扩展:添加新功能只需一行代码
  • ✅ 更易维护:代码结构清晰,职责分明
  • ✅ 更加健壮:类型安全,不易出错
  • ✅ 更易测试:每个部分都可独立测试

如果你的项目中有类似的 switch-case 工厂代码,不妨考虑使用自注册工厂模式进行重构!

相关推荐
CodeCaptain1 天前
CocosCreator3.8.x 解析Tiled1.4.x【瓦片图层、对象图层、图像图层、组图层】的核心原理
经验分享·游戏·typescript·cocos2d
树叶会结冰1 天前
TypeScript---对象:不自在但实在
前端·javascript·typescript
江公望1 天前
tsconfig配置文件增加“esModuleInterop“: true,有什么作用?
typescript
wordbaby1 天前
三个核心概念,帮你彻底打通 TypeScript 泛型
前端·typescript
guangzan1 天前
Zod:TypeScript 类型守卫与数据验证
ai·typescript·zod
乾元2 天前
边缘计算网络的自动流量分配与用户感知 QoE 优化——从“链路最优”到“体验最优”的网络控制闭环
运维·网络·人工智能·网络协议·重构·边缘计算
Ama_tor2 天前
pycharm|学会模块(包)编码模式+旧.py重构+struture管理面板
数据库·重构·pycharm
gorgeous(๑>؂<๑)2 天前
【中国科学技术大学-傅雪阳组-ICCV25】解耦重构:通过主动特征解纠缠与可逆融合实现高质量超高清图像修复
人工智能·重构
许泽宇的技术分享2 天前
打破AI调用壁垒:Antigravity Tools如何用Rust+Tauri重构你的AI工作流
人工智能·重构·rust