策略模式解耦分账逻辑,新增规则只需添加策略类并注册,无需修改主逻辑;内置Percentage、Fixed、Ladder、Recursive四策略分别应对比例、定额、阶梯、递归等不同业务场景。

直接用策略模式解耦,比硬写 if-else 或 switch 强得多——分账规则一变就得改主逻辑,而策略模式下新增一种分账方式,只用加一个类、注册一下,其他代码完全不动。
为什么不能把分账逻辑塞进 Controller 或 Service 里
常见错误是把平台抽成、作者分成、阶梯奖励、多级代理这些规则全堆在某个 calculateSplit() 方法里。结果就是:
- 每次产品提“再加个 VIP 渠道分润”就得翻出几百行代码找位置补逻辑
- 单元测试难写,一个函数覆盖七八种场景,mock 条件爆炸
- 金额计算出错时,debug 要逐层跳进嵌套条件,根本分不清是比例算错、区间判错,还是递归终止条件漏了
策略模式的核心价值不是“高大上”,而是让每种规则有自己独立的类、自己的单元测试、自己的边界处理。比如 Ladder 类只管区间匹配和比例查表,Recursive 类只管上一层净额怎么传给下一层,互不污染。
四个内置策略的实际使用差异
组件已封装好 Percentage、Fixed、Ladder、Recursive 四种策略,它们不是“功能差不多”,而是解决完全不同的业务约束:
-
Percentage:适合平台抽 10%、作者拿 90% 这类固定比例,但要注意传入比例总和必须 ≤ 100%,否则会抛InvalidAllocationException -
Fixed:适合代理拿固定 50 元/单,或作者保底 200 元,但总额不能超原始金额,否则触发校验失败 -
Ladder:配置是数组形式的区间规则,例如[[0, 1000, 0.05], [1000, 5000, 0.1], [5000, null, 0.15]],注意区间左闭右开,且必须按顺序排列,否则结果不可预期 -
Recursive:用于“一级代理抽 5%,剩余给二级代理,二级再抽 8%”这类链式分润,它返回的是每层的净收益(即扣掉上层之后的金额),不是比例本身
自定义策略必须实现 StrategyInterface
比如要加一个“仅作者独享全部”的策略,不能直接改已有类,而是新建一个类:
class AuthorOnlyStrategy implements StrategyInterface
{
public function split(SplitContext $context): Allocation
{
$allocation = new Allocation();
$allocation->add('author', $context->getTotalAmount());
return $allocation;
}
}
然后注册到 Splitter:
$splitter = new Splitter();
$splitter->register('author_only', new AuthorOnlyStrategy());
调用时和其他策略完全一致:$splitter->split('author_only', $context)。关键点在于:
- 所有策略接收的都是 SplitContext,不是原始数组或字符串,避免重复解析
- 返回必须是 Allocation 对象,保证后续统一转 toArray() 或存库格式
- 不要在这个类里做 DB 查询或 HTTP 请求,策略应是纯计算逻辑
容易被忽略的边界与精度问题
财务场景对精度和异常反馈极其敏感,这几个点上线前必须验证:
- 所有金额字段必须用 float 或 string(避免 double 精度丢失),组件内部默认用 bcadd/bcmul 做高精度运算
- Ladder 策略中如果传入金额为 0 或负数,会直接返回空分配,不会报错,需业务层提前拦截
- Recursive 在层级过深(如 >10 层)时会主动中断并抛异常,防止栈溢出,这个阈值可配置但不建议调高
- 最终分账结果的总和可能因四舍五入与原始总额差 1 分钱,组件提供 adjustLastItem() 方法自动抹平,但得明确谁来承担这 1 分——通常由平台兜底
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











