公司中项目思考的问题(二)

这篇是在工作中对代码进行重构的一些收获;

一、后端项目MVC和组件形式在业务逻辑引用上的对比

组件化对项目结构一个很重要的改变就是将原先router和control之间复杂的引用关系进行了清晰的整理,将引用关系隔离在了组件和组件之间,组件和router的关系变为垂直型关系。

可以看做是对原先control的一种以某个主体为核心的组织细化,将有相关性的逻辑进行聚合,将其逻辑限制在组件内部。

路由和逻辑之间的联系关系由原先的两层关系变为三层关系,中间的组件层将交织在一起的复杂引用进行了整理和分类,将control之间的引用关系提升到了组件层,这种逻辑的内聚是组件之间解耦的直接体现。

二、服务端端代码一个接口的代码流程分块

上面的流程基本就是一个后端接口比较好的结构划分,可以看到也是大致以三层的形式进行一个划分,这种划分方式非常清晰,可扩展的方面非常多,但是不可生搬硬套,任何东西都强写成三层,根据实际情况灵活掌握。

后端接口并不是说所有的代码逻辑都使用这种形式,很多简要的接口并不需要,这种形式对于系统结构的清晰和规划有非常明显的好处和效果,中间层既可以作为两个层之间的过渡层级,进行逻辑整理和优化,也可以作为核心层级,连接两个不相关的部分,从更宏观上说,甚至可以看做是一个巨大的适配器,帮助适配器左右的双方完成了在自己内部不适合完成的工作,是一种代码解耦的非常好的体现。

下面是三层结构的一些整理:

三、自己重构的三个小模块思路分享

1、外教积分模块

现有代码结构图:

1.1、要点:积分的整体逻辑其实非常简单,没有什么难点,唯一麻烦的地方是有非常多导致积分变化的因素。

1.2、需求简要介绍:外教在上课过程中会得到积分奖励,有很多的因素会使外教的积分增加或减少,外教成功上课数会影响积分的加倍数量,外教积分总数会影响level变化,level会对薪资造成影响,这就是大致的需求,详细需求可以在—–产品文档中搜索”积分”两个字有详细的说明。

1.3、原代码:在原始的代码中,提供了五个入口对积分进行操作,有的入口直接就更新了积分的累计数量,有的入口在等到要更新教师积分时才去更新累计数,累计数level的计算位置也不统一,booking也就是约课相关的事件是最多的,其中要涉及到很多的类型,原代码中直接使用了大量的 if 条件去判断每一个类型。

1.4、分析修改思路:积分变化的多种类型全部都是并行的,相互之间并不会产生影响,并且积分变化对外来说其实是一个操作,并不适合提供多个入口,对外提供入口的要求就是简单明确,积分中类型简要归类为五类,因此就使用五种类型作为参数,进入逻辑之后通过类型走不同的类型逻辑,得出初始积分变化量,因为后续还会有对积分数量产生影响的地方,因此不能直接在里面进行计算,类型逻辑完成之后得出产出,然后数据产出进行下面的三个逻辑,这三个逻辑分别计算累计量、最终变化量、level变化,每个流程只做一件事情,最后统一进行外教数据的更新操作。其实整理之后的代码量并没有减少,反而还增加了接近100行,在整体流程和逻辑上更加清晰一些,每种变化类型归类在某个大的类型当中,将其任何变化都集中在里面,可以看做是另一个层面的组件化。

2、邮件发送模块

现有代码结构图:简介:1、2、3、4分别是四个方法,得出四个字符串,然后这四个字符串拼接就成为一个邮件的模板名称。

2.1、要点:邮件的麻烦之处是调用的地方非常多,要发的邮件类型也非常多,类型之间有规律可循,需要可以更加容易的扩展,还要尽量减少重复代码。

2.2、需求简要介绍:需求很简单,就是发生上面事情的时候给某个人发邮件,这些邮件里面以约课部分邮件为主,而约课的规律是很明显的。

2.3、原代码:原来是比如我这里要发邮件,那我在这里用七八行代码找出要发给谁,里面的标示信息,昵称等数据,然后开始发,还有记录一波邮件日志,基本就是到处都是重复代码。

2.4、分析修改思路:经过归类之后发现,以约课为主的邮件,需要知道的条件是哪个系统、目标人、课程类型、课程产生的变化这四个,所以在统一的计算类型阶段,将准备好的课程约课数据传入之后,分别对四种数据进行计算,得出分离的数据,然后组合成为一个邮件类型,而邮件内部使用的基础数据大多是相同的,可以进行共同的计算,特殊数据可以使用外部传入的方式,模板的替换用相同字段替换的方式,可以使用正则等,整体思路很简单,主要还是降低代码量,将邮件逻辑集中起来,并且使用分别计算一段类型的方式,扩展也较容易一些,只是需要在修改之前对代码逻辑有清晰的了解。

3、课程状态设置

现有代码结构图:逻辑只有三步:打开冰箱门,把大象放进去,关上冰箱门。

3.1、要点:状态不是很多,八九个,操作较多并且每个状态要做的操作还不一样。

3.2、需求简要介绍:课程的整个生命周期内设置完成状态之后,要进行一系列根据不同状态产生的后续操作。

3.3、分析修改思路:经过一段时间的业务逻辑整理之后发现,虽然设置状态要做的事情很多,其实每件事情之间都是相互独立的,前后会产生关联的操作很少,看过薪资重构后的代码有所启发,想到了以配置的方式对代码进行重构,提供一个对外的状态设置入口,然后一个集中的状态操作配置方法,配置完成之后一个运行逻辑对配置好的方法进行统一的调用,在开始之前就算好后续操作需要的各种基础数据,然后这些数据贯穿到每个方法之中,所有方法都要讲这个基础数据作为输入,然后将自己的产出挂在上面,并且返回这个基础数据,每个方法都在自己的内部判断自己的业务逻辑需要的基础数据是否充足,不足直接抛错。经过植入机构需求之后发现这种方式是非常容易扩展的,不需要像原先一样改一处地方要仔细看一下整个流程对不对,只要专注于自己的那个方法,然后将配置文件改掉就可以了

准备好基础数据之后,直接通过状态得到要执行的操作流程和要校验的数据,然后通过kernel方法执行这个配置好的流程即可,每个方法之间最多只会产生数据依赖,扩展性和可读性大大提高。

四、思考

将逻辑看做一个黑盒,不同逻辑之间通过data进行相互的联系,每个逻辑在自己内部判断支撑自己运行需要的必要数据是否传入,然后根据自己的逻辑和传入的数据将自己的产出加在data中返回,只和自己逻辑相关的修改操作在逻辑内执行完毕之后,添加data让后续逻辑知道自己这边操作是否成功,自己这块逻辑的产出是什么,这样的好处是数据的前期准备可以集中起来,后续逻辑不断增加data的体积,也就是说操作解耦分块,数据贯穿所有操作。
       优点:容易扩展、流程清晰、数据出问题肯定是操作导致的,而操作是完全独立在一个方法中的,所以容易定位。
       缺点:数据是从一开始确定的,所以需要编写人员对data里面包含了什么数据有清晰的了解,要清楚运行的逻辑、配置文件稍长,如果一件事情多种状态都要使用,那么每个配置都要写一遍。
逻辑通常来说有两种,并行的逻辑和串行的逻辑:
       并行逻辑:并行逻辑之间无依赖,适合使用配置的方式,功能的适应性和扩展性更强。
       串行逻辑:逻辑流程通常由某些核心数据集中控制,那就要数据贯穿整个流程,一开始就将所有需要的数据准备好,而不是等到用的时候再去查,即用即查会导致一些重复的查询,并且数据不集中,不知道我现在查到的数据下面的流程是否要使用,或者上面的流程是否已经查询过。

五、想法

5.1、当一个逻辑牵涉到的条件非常多的时候,无论如何去组织代码,总的复杂度都不会有明显的降低,只是说代码在经过人为组织之后流程更加清晰,更易读也更加容易理解,维护也变得简单。
5.2、并行和串行的代码组织方式各有各的优势,通常来说开发中要将这两种方式结合使用。
5.3、在逻辑入口处统一进行数据准备,可以防止后面需要什么查什么导致的混乱,一旦出现问题可以在数据准备的地方进行统一的查看,不需要把时间浪费在找哪里建立的数据有问题上面。
5.4、一个大的逻辑块不要使用过多的入口,减少入口的数量,通过参数和类型来控制内部的逻辑。
5.5、每一块逻辑从data重拿到自己需要的,把自己的产出放在data上走入下一个流程。

5.6、

        状态多流程少的时候:将状态的判断放入流程当中,流程内每个方法之间相互独立,通过数据和状态控制方法的输入和产出
        流程多状态少的时候:
                每种状态对应的流程都一样:使用流程控制状态的方式,
                每种状态对应的流程不一样:使用状态控制流程的方式,配置化每个状态要做的事情

5.7、流程清晰程度是一件带主观性的事情,思考问题时考虑的着眼点、方式、切入点等不同,对于什么样是更清晰的组织方式都会产生不同的见解。