
4.5 工厂模式重构代码
接下来使用工厂模式优化代码,也算是一次代码重构。当整理代码流程并重构后,会发现代码结构更清晰了,也具备了应对下次新增业务需求的扩展性。
注意:以下这段代码重构只是抽离出最核心的部分,方便理解和学习。在实际的业务开发中,还需要额外添加一些其他逻辑,在使用上进行完善,例如调用方式、参数校验和Spring注入等。
4.5.1 工程结构


从上面的工程结构来看,是否有一种感觉:这样的工程看上去更清晰,类的职责更明确,分层可以更好地扩展,可以通过类名就能大概知道每个类的功能。如果暂时还无法理解为什么要这样修改也没有关系,通过源码进行实战操作几次,就可以慢慢掌握工厂模式的技巧了。
为了便于理解整个工程中相关类的具体作用,可以参考图4-3。

图4-3
4.5.2 定义发奖接口

·对于所有的奖品,无论是实物商品、优惠券还是第三方兑换卡(爱奇艺),都需要通过程序实现此接口并处理。这样的方式可以保证入参和出参的统一性。
·接口的入参包括:用户 ID(uId)、奖品 ID(commodityId)、业务 ID(bizId)及扩展字段(extMap),用于处理发放实物商品时的收货地址。
4.5.3 实现三种发奖接口
1.优惠券

2.实物商品


3.第三方兑换卡(爱奇艺)

·从上面代码实现中可以看到,每一种奖品的实现都包装到自己的类中,当新增、修改或删除逻辑时,都不会影响其他奖品功能的测试,可以降低回归测试和相应的连带风险。
·如果有新增的奖品,只需要按照此结构进行填充对应的实现类即可。这样的实现方式非常易于维护和扩展。
·在统一了入参及出参后,调用方不再需要关心奖品发放的内部逻辑,按照统一的方式即可处理。
4.5.4 创建商店工厂


这是一个商店的工厂实现类,里面提供了两种获取工厂实现类的方法:一种是依赖奖品类型,另一种是根据奖品类信息进行实例化。这两种方式都有自己的使用场景,按需选择即可。在第一种实现方式中用到了 if判断,这里既可以选择使用 switch语句,也可以使用map结构进行配置(key是类型值,value是具体的逻辑实现)。通过商店工厂类获取各种奖品服务,可以非常干净、整洁地处理业务逻辑代码。后续新增的奖品按照这样的结构扩展即可。另外,需要注意关于编码规范和相关工具的检查,比如p3c插件,这些插件会检查if语句是否有括号包装。本书为了让代码更加简短,也更好地展示核心逻辑,做了简化处理。在实际的业务开发中,可以补全if语句后的大括号以及会选择用equal比对。
4.5.5 测试验证
编写单元测试。


在以上的单元测试类中,商店工厂类中的两个方法都写好了各自的测试代码。接下来验证并查看运行结果。
testStoreFactory01(),测试结果。


testStoreFactory02(),测试结果。

从运行结果可以看到,这两种获取工厂实现的接口都可以满足业务需求。在实际使用中按需选择即可。这段重构后的代码既满足了业务方和产品经理的需求,也满足了研发人员对代码质量的追求。另外,从运行的测试结果也可以看出来,在进行封装后,这样一整套发放奖品服务有统一的入参、统一的结果。既提高了代码的结构性,也让工程易于维护和扩展。