在有一些业务代码里面,写了一堆if-else,而里面的业务代码都在做差不多的事情。典型的例子就像以前的社保系统,人员异动的时候根据异动类型来判断,然后到不同的方法里面去执行,有可能方法还有重载方法,导致整个类的代码非常多,下一个人来维护的时候非常困难。
所以,根据我在青海项目里面写的一个方法,然后分析了一下参投保系统的人员异动代码,有了以下优化:
1、策略模式
其主要目的是将重复的业务代码,写成一个“策略”,然后根据传入不同的对象,去调用。就像在参投保系统里面,人员新参保,暂停参保,终止参保等这些异动类型,都可以写成一个策略,写在不同的类里面,就可以起到第一阶段的解耦作用,再也不用在同一个类里面找方法了。一个类就做一件事,就非常符合”单一职则”原则,而同时一个异动类型要修改,就去修改某个指定的类,在后期的维护上,也是非常方便的。
2、工厂模式
工厂模式在这里,是用来构建对象,但是在这个demo里面,主要是用来消除if-else
下面就用代码来演示
/*** @description 策略接口,用来定义有多少种策略,比如在广铁系统里面,可以定义 异动,异动提交/审核,异动受理* @author: huangyeqin* @create : 2020/12/14 8:36*/public interface IPersonChangeStrategy<T> extends InitializingBean {// 异动申请: T 表示从前台传过来的数据 bizType表示异动类型void apply(T t,String bizType);// 异动审核 :特别说明,一个异动,从申请到审核受理,最好用一个对象类接受数据void audit(T t,String bizType);// 异动受理void submit(T t,String bizType);}/*** @description 策略工厂这个类的目的,是用来初始化策略实现类的* @author: huangyeqin* @create : 2020/12/14 16:01*/public class IPersonChnageFactory {private static Map<String, IPersonChangeStrategy> strategyMap = Maps.newHashMap();public static IPersonChangeStrategy getInvokeStrategy(String name) {return strategyMap.get(name);}public static void register(String name, IPersonChangeStrategy strategy) {if (StringUtils.isEmpty(name) || null == strategy) {throw new HygeiaException(404,"请传入正确的类型");}strategyMap.put(name, strategy);}}/*** @description 策略上下文,用来执行策略* @author: huangyeqin* @create : 2020/12/14 8:38*/public class AuthContext<T> {private IPersonChangeStrategy strategy;public AuthContext(IPersonChangeStrategy strategy){this.strategy = strategy;}// ... 这个是暴露出去的接口,所以这里可以将很多方法都放在这个方法里面执行,就成了模板方法模式public void apply(T t,String bizType){strategy.apply(t,bizType);}//... 省略了 审核、受理的方法}/*** @description 具体的策略实现类* 这里有个条件,需要加载spring框架。因为没有spring框架,这些类是没办法交给spring去管理,所以不能提前初始化,那么策略工厂就无法获取到这个类* @author: huangyeqin* @create : 2020/12/14 15:55*/@Componentpublic class AuthAlterationStrategy implements IPersonChangeStrategy<PmmAlterationOptAddDTO> {@Overridepublic void doHandler(PmmAlterationOptAddDTO pmmAlterationOptAddDTO, String bizType) {}// 要注意的是,所有的异动类型,都需要提前维护一个枚举类,如果没有枚举类,也可以维护一个 常量类,@Overridepublic void afterPropertiesSet() throws Exception {IPersonChnageFactory.register(WorkFlowEnum.PMM003.getFlowCode(),this);}}// .... 可以写多个策略实现类// 开始调用策略public class MainTest{public static void main(String[] args){// 在调用的时候,bizType传入一个异动类型就可以,object是从前端来的参数,所以前端所有的方法,都可以调用这个接口了,// 真正的实现了所有的异动,都是一个入口,甚至以后的异动,都只需要一个按钮,想要做什么异动,在下拉框中选一个异动类型,然后填数据,// 可以做到让用户少判断,所有的逻辑都写在策略类里面IPersonChangeStrategy strategy = IAuthFactory.getInvokeStrategy(bizType);strategy.doHandler(object, bizType);}}
以上就是策略模式+工厂模式在广铁项目中的使用。
说完了使用,再说说优缺点:
优点
(1)逻辑解耦,再也不需要在一个类里面写几十个方法了,甚至重载的方法一大堆。
(2)符合单一职责原则,就是一个类就负责一件事。
(3)符合开闭原则;如果想要增加一个业务类型,直接加一个策略类就可以了。比如说广州市社保局要加一个退休延缴的异动类型,那么直接加一个策略类,然后专注的去实现业务代码;
(4)代码逻辑清晰,再也不会点一个方法跳来跳去,甚至很多时候根本找不到方法在哪里。
缺点:
(1)类变多了,但出现几十个异动类型的时候(当然一般不会有这么多,最多就十几个),会写很多类,但这也是有好处的,已经这么大一个项目了,多写几个类也不影响什么。
(2)要解决事务问题,因为参投保系统的这个框架不熟悉,不知道事务是在哪里配置的,所以要先解决事务问题,不然程序执行中断的时候,事务不会回滚,会造成数据错误。但这个如果解决了以后就不是问题。
(3)最关键的一点,就是逻辑不会消失,只会从一个地方转移到另一个地方。
上面的代码是很完美的,但是没有考虑一个问题,如果没有spring环境该怎么办?
没有spring环境的话,代码确实无法做到那样完美,但也不是没有办法解决的,spring的InitializingBean 主要是在初始化的时候,将策略放到map集合中,那么只要做到类似的效果就行了,下面我就来推荐一种枚举类的写法。
/*** @description 策略模式枚举类* @author: huangyeqin* @create : 2020/12/16 10:19*/public enum StrategyEnum {ZHAOYUN_STRATEGY("ConcreteStrategy",new ConcreteStrategy()),GUANYU_STRATEGY("Concrete2Strategy",new Concrete2Strategy());private IStrategy strategy;private String name;StrategyEnum(String name,IStrategy strategy){this.name = name;this.strategy = strategy;}public IStrategy getStrategy() {return strategy;}public void setStrategy(IStrategy strategy) {this.strategy = strategy;}public String getName() {return name;}public void setName(String name) {this.name = name;}/*** 根据name获取strategy的值* @Desc :* @Author : huangyeqin* @Date : 2020/12/16 10:26* @Param : code* @Result : com.outlets.design.strategy.IStrategy*/public static IStrategy getStrategyByName(String code){for(StrategyEnum platformFree:StrategyEnum.values()){if(code.equals(platformFree.getName())){return platformFree.getStrategy();}}return null;}}
上面的枚举类中,将name设置为key,策略设置为value,也实现了map的效果。
那么在客户端中如何去调用呢?
/*** @description 客户端* @author: huangyeqin* @create : 2020/12/16 10:09*/public class Client {public static void main(String[] args) {String name = "zhaoyun";StrategyContext context = new StrategyContext(StrategyEnum.getStrategyByName(name));context.handler();}}
同样的效果,也不用去写那么多的if-else了。
写了这么多,并不是说不推荐用if-else,而是不推荐在哪里都用if-else,特别是当代码块特别长的时候,要考虑一下接手这个代码的人的心情。
