You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

服务端下发动态Action时如何避免switch不违反开闭原则OCP

实现方案

核心思路是用字符串到动作构造器的映射表代替硬编码的switch判断,新增动作时只需要新增对应实现类并注册到映射表即可,不需要修改原有解析逻辑,完全符合开闭原则。

第一步:扩展Actionable协议,支持从服务端下发的Action结构体初始化

因为每个动作需要拿到服务端下发的额外参数(比如跳转页面需要传的ID、弹窗需要的文案等),所以先给协议加初始化要求:

protocol Actionable {
    init?(rawAction: Action)
    func execute()
}

第二步:实现各业务动作类

每个动作单独实现Actionable协议,和原有逻辑一致:

// 跳转到ScreenA的动作实现
struct GoToScreenAAction: Actionable {
    private let productId: String
    
    init?(rawAction: Action) {
        // 从rawAction里解析当前动作需要的参数,解析失败返回nil
        guard let productId = rawAction.extraParams?["productId"] as? String else {
            return nil
        }
        self.productId = productId
    }
    
    func execute() {
        // 原有跳转ScreenA的逻辑,携带productId参数
        let screenA = ScreenAViewController(productId: productId)
        currentNavigationController?.pushViewController(screenA, animated: true)
    }
}

// 弹窗动作实现
struct PresentAlertAction: Actionable {
    private let title: String
    private let content: String
    
    init?(rawAction: Action) {
        guard let title = rawAction.extraParams?["title"] as? String,
              let content = rawAction.extraParams?["content"] as? String else {
            return nil
        }
        self.title = title
        self.content = content
    }
    
    func execute() {
        let alert = UIAlertController(title: title, message: content, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "确定", style: .default))
        currentViewController?.present(alert, animated: true)
    }
}

第三步:实现动作注册表,替代switch判断

用一个全局字典维护动作类型字符串和对应Actionable实现类的映射关系,新增动作只需要注册新的映射即可:

class ActionRegistry {
    static let shared = ActionRegistry()
    private var actionTypeMap: [String: Actionable.Type] = [:]
    
    private init() {
        // 初始化时注册所有支持的动作
        registerAction(type: "goToScreenA", actionClass: GoToScreenAAction.self)
        registerAction(type: "goToScreenB", actionClass: GoToScreenBAction.self)
        registerAction(type: "presentAlert", actionClass: PresentAlertAction.self)
    }
    
    /// 注册动作类型和对应实现类
    func registerAction(type: String, actionClass: Actionable.Type) {
        actionTypeMap[type] = actionClass
    }
    
    /// 把服务端下发的原始Action转为可执行的Actionable实例
    func resolveAction(rawAction: Action) -> Actionable? {
        guard let actionClass = actionTypeMap[rawAction.type] else {
            return nil
        }
        return actionClass.init(rawAction: rawAction)
    }
}

第四步:改造ActionResolver,支持直接处理服务端下发的原始Action

class ActionResolver {
    static func execute(rawAction: Action) {
        guard let actionable = ActionRegistry.shared.resolveAction(rawAction: rawAction) else {
            // 不存在的动作类型直接忽略,可加日志埋点排查问题
            debugPrint("unsupported action type: \(rawAction.type)")
            return
        }
        actionable.execute()
    }
}

最终调用方式

用户点击触发动作时,直接传入服务端下发的原始Action即可:

func onButtonClick(rawAction: Action) {
    ActionResolver.execute(rawAction: rawAction)
}

新增动作的流程

后续新增动作完全不需要修改原有解析逻辑,只需要两步:

  1. 新增一个实现Actionable协议的结构体,写对应业务逻辑
  2. 在ActionRegistry的初始化方法里新增一行注册代码

该方案完全符合开闭原则,没有硬编码的switch/if判断,所有动作逻辑内聚在各自的实现类里,便于维护和单元测试。


内容的提问来源于stack exchange,提问作者Hoven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 16:15:04