使用事件机制,可以很好的实现复杂交互逻辑中的 OCP。(OCP,开放封闭原则)
首先,对复杂的交互逻辑进行子域划分。哪怕这段逻辑其实已经属于一个子域了,如果还是太过复杂,可尝试进一步划分。
不同的子域,针对的交互逻辑并不相同。每个子域对应一个模块,每个模块的逻辑都是自洽的,所以这实现了封闭的原则。
每个模块开放的原则,则通过事件机制驱动其他模块的业务逻辑。本模块的参数更新,则通过事件直接 setter。
在建模过程中,事件风暴是一种很好的方法。事件风暴先整理出业务流程中所有可能的事件,然后再去分析事件的发起对象。如果能对事件归类到不同子域中,则可以进一步降低实现的复杂度。
以我做的战斗系统分析为例:
player 执行 action, action 在定时帧事件中创建 skill。skill 在释放前,则由 player 的 属性和 buff 进行增益。
随后 skill 通过判定规则进行判定,命中后对 target 添加 buff 和增益。 target 计算增益,发出被攻击事件和死亡事件。
所有的业务逻辑都放到一个模块下相互调用,非常复杂,也难以实现 OCP。如果我们做出子域划分,例如:
- 模块:战斗动作和过程,只需要关注 player, action, 简单的 skill 状态和运动。 这个模块会产生以下事件,动作开始,动作结束,技能创建。
- 模块:skill 增益,在收到技能创建事件后,创建此模块对应的 skill 概念,并按 player 数值更新 skill 数值。如果对战斗过程的 skill 有影响,则通过事件去更新相应的运动属性。
- 模块:技能判定,在事件帧判定技能物理上覆盖的目标,并结合队伍,阵营,声望等概念判定目标清单,然后生成判定事件。
skill 增益模块在收到判定事件后,则生成添加 buff 和增益事件。
- 模块:buff 模块,接收添加 buff 事件,管理 buff 生命周期。buff 通过增益事件影响
- 模块:数值模块,接收增益事件,根据公式计算伤害,并产生以下事件,受到攻击,死亡等等。
如此,则把整个战斗系统划分为多个独立的子域,每个子域封装一部分十分内聚的业务规则。越内聚越封闭,而开放性,可以通过新模块和事件参与其中,进行调整。
比如现在想对战斗动作进行提速,可以增加一个动作增益模块,进行增益计算,并通过事件更新战斗动作速率。
比如现在增加一个装备系统,装备生成添加 buff 事件和增益事件。
如果真遇到需要打开原来模块的封闭性才能实现的功能,也不能死板的按照新增一个模块的方法。比如 cooldown 系统,在 action 执行前必须要判断的,所以是紧凑属于战斗过程模块的。