图片来自Pexels如何优化这些if-else?本文分享一种设计模式:责任树模式。通过将责任链和策略模式相结合,成为广义的责任链模式,既可以完成任务的逐级委托,又可以在任意层次选择不同的下游策略进行处理,抽象出责任树模式变成了一个通用的框架。扪心自问,写业务代码时是否也习惯了堆砌if-else?问题背景最近开发一个需求,接口需要根据多个输入参数p1,p2,p3的不同组合,版本根据其对应的业务策略给出结果数据。由于接口已经开发了三个阶段,所以每次开发新阶段的需求时,为了兼容旧的业务逻辑,大家往往不删改,而是增加。因此,这段代码已经产生了一些“恶臭”。函数入口通过不断添加“guard语句”判断版本来跳转到新的业务逻辑方法。各个时期的业务逻辑也通过p1、p2、p3的if-else组合形成不同的分支逻辑。这已经是我的简化表达了。总之,对于我一个新同学来说,整理这段业务代码还是费了一番功夫的。而且,这段逻辑相当于一个通用的业务能力。未来肯定会有第五、第六、N相的需求,输入参数的值会不断扩大。“不良品味”会越来越严重。总结一下,当前场景面临的问题是:如何在保证兼容旧版本的同时,解决接口升级,轻松开发新版本业务逻辑?如何根据输入参数p1、p2、p3等的不同组合进行策略定位?解决方案在思考解决方案时,很容易想到两种可以优化相似场景的设计模式:责任链模式和策略模式。ChainofResponsibilityMode责任链模式实现了类似于“流水线”结构的逐级处理,通常是链式结构,将“抽象处理器”的不同实现串联起来。如果当前节点可以处理任务,则直接处理。如果无法处理,则委托给责任链中的下一个节点,以此类推,直到有节点可以处理该任务。我们可以通过责任链模式完成不同版本业务逻辑隔离的处理,比如节点1处理version=1的请求,节点2处理version=2的请求,等等。但问题是我们遇到的场景还是需要按照一定的策略路由到不同的下游节点去处理。这就是策略模式擅长解决的问题。策略模式策略模式的目的是解耦算法的使用和定义,使其可以根据规则路由到不同的策略类进行处理。我们可以使用策略模式来解决根据不同的参数组合执行不同的业务逻辑的场景。但是在我们的场景中,仅仅一层策略路由是无法满足任务处理需求的。请求的分层处理又是责任链模式擅长的。可以看出,这两种设计模式都不能完全契合当前场景:责任链模式可以实现逐级委托,但不能像策略模式那样每一级都路由到不同的处理器;策略模式通常只有一层路由,不容易实现多个参数的策略组合。因此,我们自然可以想到:是否可以将两种模式结合起来?广义责任链模式:责任树模式将责任链与策略模式融合,成为广义责任链模式,我简称为“责任树”。model”。这种模式不仅可以完成任务的逐级委派,还可以在任意层级选择不同的下游策略进行处理。那么问题来了,如何通过责任树模型解决我们之前遇到的问题?首先我们看第一个问题,新旧接口的隔离和兼容如何解决:每个版本接口的逻辑都可以作为一个责任树中第一层的不同实现,比如对应上图中的Strategy1、Strategy2、Strategy3节点,这样在接口入口处,先将policy路由到不同的分支,不再是没有节点命中就直接向下游delegate返回error,然后第二个问题,参数的组合定位在不同的策略实现上:同一个思路,一个参数对应责任树上一层的路由,参数的不同值可以路由到下一层的不同实现,这样逐级委托,新增入参的枚举值,甚至是新入参的扩展都可以很方便。优化收益通过“责任树模型”重构该业务后,可以获得以下收益:降低后续迭代的人力成本。代码结构更清晰,可维护性提高:没有各种可维护性差的“guard语句”&巨方法的跳转,功能可以收敛在理想的50行以内。后续修改代码的新需求不容易出错:策略之间隔离,不需要读大函数理清逻辑再修改,只需要增加一个路由+一个新的策略实现方法想都没想。易于定位问题:同样由于策略间的隔离,调试时可以直接定位到指定策略的业务逻辑代码,无需逐句查看。相信有开发经验的同学应该都有体会。即使是自己写的代码,一时不看也会忘记。当有其他修改时,需要按照代码的逻辑进行。那就更酸了。因此,对庞大的功能进行拆分和解耦是非常重要的。虽然抽象框架通过“责任树模型”解决了我在这个需求开发中遇到的问题,但是类似的问题仍然普遍存在。本着助(shǎo)人(zào)(lún)幸福(zi)的精神,我更进一步,将责任树模型抽象成一个通用的框架,让大家在遇到类似问题时可以快速“种树”.这个框架由一个Router和Handler组成:Router是一个抽象类,负责定义如何路由到下游的多个子节点。Handler是一个接口,负责实现各个节点的业务逻辑。通过Router和Handler的组合,我们可以很容易的组装出整个树状结构。从图中可以看出以下几个关键点:除根节点(入口)外,每个节点都实现了Handler接口。根节点只继承Router抽象类。所有叶子节点只实现Handler接口,不继承Router抽象类(不需要向下委托)。除了根节点和叶子节点,其他节点是上层的Handler和下层的Router。那么话不多说,先看框架代码。①AbstractStrategyRouter抽象类:/***通用的“策略树”框架,通过树结构实现分发和委托,每一层通过指定的参数向下分发委托,直到到达最终的执行者。*框架包含两个类:{@codeStrategyHandler}和{@codeAbstractStrategyRouter}*其中:实现{@codeAbstractStrategyRouter}抽象类完成策略分发,*实现{@codeStrategyHandler}接口实现策略。*第二层的A、B等节点既是Root节点的策略执行者,又是策略A1、A2、B1、B2的分发者。这样的节点只需要继承{@codeStrategyHandler},同时实现{@codeAbstractStrategyRouter}接口即可。**
*+--------+*|Root|------------第1层策略入口*+--------+*/\--------------根据词条P1分配策略*/\*+------++------+*|A||B|-------第二层不同策略的实现*+-----++-----+*/\/\----------根据输入参数P2的策略分发*/\/\*+---++---++---++---+*|A1||A2||B1||B2|-----layer3种不同的策略实现的*+---++---++---++---+***@author*@date*@seeStrategyHandler*/@ComponentpublicabstractclassAbstractStrategyRouter