共计 2091 个字符,预计需要花费 6 分钟才能阅读完成。
1. 当 Skill 遇上 Rule:为什么开发者总是搞混?
在电商促销系统开发中,我见过团队用 Rule 硬编码满减计算逻辑(if order.total>100 then discount=20),结果每次新增营销活动都要修改核心规则引擎。另一个反面案例是把用户画像分析这种动态能力写成 Rule,导致每次请求都要重新计算用户等级。这种混淆会导致:

- 逻辑腐化 :订单服务里混杂着
applyVIPSkill()和checkBlacklistRule(),维护时像走迷宫 - 性能雪崩:把实时风控 Skill 当作 Rule 预加载,内存占用飙升 40%
- 扩展困境:新产品线要求「会员生日特权」,却发现现有 Rule 体系无法扩展新条件类型
2. 概念拆解:一张表看清本质差异
| 维度 | Skill(技能) | Rule(规则) |
|---|---|---|
| 本质 | 业务能力单元(如支付、风控) | 约束条件(如年龄≥18 才可购买) |
| 执行时机 | 运行时动态触发 | 预加载 / 请求时静态校验 |
| 作用域 | 绑定具体对象(用户 / 订单) | 全局生效(所有 SKU 限购策略) |
| 可变性 | 支持热更新(如算法模型切换) | 通常需重启生效 |
关键记忆点:Rule 像交通灯(无条件执行),Skill 像驾驶员(需要判断路况)
3. 代码实战:从分离到协作
3.1 Skill 的模块化实现(Python 示例)
class DiscountSkill(ABC):
"""抽象技能:所有折扣计算能力需实现此接口"""
@abstractmethod
def calculate(self, order: Order) -> float:
pass
class VIPDiscountSkill(DiscountSkill):
def __init__(self, user: User):
self.user = user # 技能绑定具体用户
def calculate(self, order: Order) -> float:
"""根据用户等级动态计算折扣"""
if self.user.level == 'gold':
return order.amount * 0.2 # 黄金会员 8 折
return 0
# 单元测试验证技能独立性
def test_vip_skill():
mock_user = User(level='gold')
skill = VIPDiscountSkill(mock_user)
assert skill.calculate(Order(amount=100)) == 20
3.2 Rule 的 DSL 配置(YAML 示例)
rules:
- name: "adult_only"
condition: "user.age >= 18" # 布尔表达式
actions:
- "deny('Underage purchase')"
- name: "flash_sale_limit"
condition: "item.stock < 10 && cart.quantity > 2"
actions:
- "adjust_quantity(max=2)"
3.3 协同设计:策略模式 + 规则引擎
// 策略工厂根据 Rule 结果选择 Skill 实现
public class PricingStrategyFactory {
private RuleEngine engine; // 注入规则引擎
public PricingStrategy createStrategy(User user, Item item) {if (engine.check("premium_member", user, item)) {return new VIPPricingSkill(user); // 动态技能
}
return new StandardPricing();}
}
4. 生产环境生存指南
4.1 Rule 性能优化三把斧
- 预编译规则:将 DSL 转换为 AST(抽象语法树),避免每次解析
- 条件排序:把高频触发规则(如库存检查)放在最前
- 短路评估 :
condition: "A && B"中 A 为 false 时不计算 B
4.2 Skill 安全控制
- 权限标签:
@Require(role='FRAUD_ANALYST') - 输入消毒:对技能输入参数做 Schema 校验
- 沙箱执行:高风险技能(如 AI 推荐)在独立容器运行
4.3 调试技巧
- Rule 可视化:生成
rule_name → condition → action流程图 - 上下文快照:记录触发 Rule 时的完整系统状态
- 染色标记:给特定用户会话添加
debug_rule=adult_only参数
5. 血泪教训:三大经典踩坑实录
-
陷阱:Rule 条件嵌套过深
❌ 错误示例:if (A && (B || C)) && !D.getE().isF()
✅ 解决方案:拆分为多个原子规则,用规则链串联 -
陷阱:Skill 携带隐藏状态
❌ 错误示例:技能内部缓存用户行为历史导致内存泄漏
✅ 解决方案:显式传入上下文对象,保持无状态 -
陷阱:混淆业务异常与 Rule 阻断
❌ 错误示例:用 Rule 抛出InsufficientBalanceException
✅ 解决方案:Rule 只返回true/false,业务异常由 Skill 处理
思考题
当 Skill 需要升级时,如何设计版本兼容机制才能保证:
– 旧 Rule 能继续调用 v1 技能
– 灰度发布期间新 Rule 可调用 v2 技能
– 回滚时不丢失中间状态?
(提示:考虑技能版本路由表与规则的条件分支)
正文完
