某次OO重构笔记

大部分业务产品的发展都是从简单单一的功能,逐步走向复杂的、多样化的功能,试用中心也不例外。

从申请这个功能可见一斑,申请表示用户申请某一个试用品,系统所做的事情就是接受一个Post请求,解析参数、校验参数、用户、试用品的合法性,校验通过之后保存申请记录(即用户id+试用品id+其他信息)。对于这样单一的功能,按照过程化的编码方式去实现很直观、简单,也没有遇到什么问题。

但是随着试用品种类的丰富,出现了很多不一样的申请。比如接受的参数不一样,对用户的校验规则不一样,需要保存的数据不一样,要调用的服务不一样等等。按照面向过程的编码方式,会在代码中充斥着很多if else。每加入一种新的申请玩法,就要改变原来的代码,在行为有区别的地方加入一个if else的分支处理。这样下去代码越来越难维护,容易出错,需要回归测试的功能点也会比较多。于是我们决定重构,转换编码思路,使用面向对象的设计方法。

通过对申请这个功能行为进行抽象,可以分为两步,第一步预申请,第二步是申请。预申请做的事情是进行问答校验、还有用户校验、试用品合法性等校验,预申请通过之后,才会让用户开始填写申请的表单,提交申请。由于是分开的两步请求,从安全的角度考虑,预申请所做的任何校验、申请都要做。基于这样的分析,我们采用了模板方法,定义了预申请和申请的模板。

预申请的模板:

预申请时用户输入参数的解析和校验

试用品状态的校验

用户相关校验

其他综合校验

 

申请的模板:

申请时用户输入参数的解析和校验

试用品状态的校验

用户相关校验

其他综合校验

申请action

 

在这样的模板下面,不同的申请玩法,具体的参数解析、校验规则和申请action会有所不同。

我们定义了一个ApplyHandler接口,接口声明的方法和模板的步骤对应。抽象类AbstractApplyHandler实现了ApplyHandler,并对其中的某些方法进行缺省实现。具体的玩法对应AbstractApplyHandler的子类,子类针对个性化的步骤进行覆盖实现。

核心类图如下:



 

 

上图ApplyFO,作为申请的统一入口,由screen/action层调用,客户代码只传入了试用品id,用户idApplyFO必须拿到对应ApplyHandler的实现类,然后执行申请的模板。

如何拿到ApplyHandler的实现类,有这么几种做法。最直接的一种办法就是,ApplyFO同时引用了所有ApplyHandler具体实现类,按照每一个ApplyHandler的执行条件写一个getApplyHandler的方法:

If(A&B)

Return  ApplyHandler1

Else if(B&C)

Return ApplyHandler2

Else if((D&E)||(G&F))

Return ApplyHandler3

…….

这样写虽然也可以,但是这个方法会充斥过多的if else,容易出错。而且加入一种新的玩法的时候,需要修改这个方法。

其实这边要做的就是一个规则映射,把符合条件的业务规则找出来,然后执行它。这个场景有点像规则引擎的场景,系统包含一个规则库,每个规则的触发都需要满足一定条件(if… then),用户把事实输入到引擎,引擎执行规则匹配,把匹配的规则找出并fire。于是我们参考这种思路,做了一个规则映射器RuleMapper

ApplyHandler可以理解成一套业务规则,对应一种申请玩法。他实现Rule接口,定义isMatch方法。每一个AbstractApplyHandler的子类都要实现isMatch方法,在这个方法里面描述符合什么样子的条件可以走自己这套业务规则。ApplyFO把规则映射功能委托给RuleMapper<AbstractApplyHandler extend Rule>RuleMapper持有一组规则的引用,通过遍历执行规则的match方法,找到符合条件的规则。当加入一种新的ApplyHandler的时候,不需要修改原来的代码,只需要实现自身的isMatch方法,通过配置的方式,把自己加入到RuleMapper的规则库里。采用这种方式就可以做到OCP原则,添加新功能的时候,不需要修改原来的代码。

 

在申请的校验中,有一步是要对用户进行校验的。用户校验包括了用户黑名单、限制列表、是否已经申请过、一天申请次数是否已满等等,还存在一些有门槛的试用品会校验用户的资格,比如校验是否是精准用户、淘金币是否足够等等。这一类用户资格校验穿插在不同的玩法中,同一种玩法也可能存在不同的用户资格校验,因此他和具体的ApplyHandler实现要解耦。

在这个场景里面,我们把对用户资格的校验抽象为一种策略,采用了策略模式,类图如下:



 

ApplyHandler持有一个UserChecker的规则映射器,ApplyHandler.userCheck()会通过规则映射器拿到对应的策略实现类,然后调用UserChecker.isOK方法来判断用户的资格。在这个类图里,UserCoinChecker表示淘金币校验策略,UserPreciseChecker表示精准用户校验的策略。UserChecker成为一个扩展点,以后可以很方便的加入更多的用户资格校验器。

猜你喜欢

转载自hill007299.iteye.com/blog/1911018
oo