100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 工厂模式+策略模式+反射机制解决系统功能模块相似的问题

工厂模式+策略模式+反射机制解决系统功能模块相似的问题

时间:2022-08-06 05:19:32

相关推荐

工厂模式+策略模式+反射机制解决系统功能模块相似的问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

背景痛点一、如何根据不同条件获取不同的数据库Mapper?二、如何根据不同条件创建相应的数据库实体对象?总结

背景

本人在项目中遇到了一种情况是,两组功能模块的service层业务逻辑几乎完全相同。只是controller层传入的实体不同,以及Dao层采用的mapper不同(本项目持久层框架采用的是Mybatis-plus)。因此我希望可以使用一套service层代码,通过参数控制获取不同的mapper来实现不同条件的数据库操作,前提是不使用 if -else 这种不符合开闭原则的代码。

痛点

针对这种情况,如果不采用设计模式,就会导致需要编写两套几乎完全相同的代码,工作量非常大,而且扩展性差,如果后续业务需要再增加一组模块,则还需要再编写一套代码,费时费力,代码冗余。因此,本人选择了采用了工程模式+策略模式以及结合了java反射机制,解决了上诉的痛点。下面我就结果我开发时候的设计思路。

提示:以下是结合我个人开发的思路顺序来记录的

一、如何根据不同条件获取不同的数据库Mapper?

上面提到,我们的service层逻辑几乎相同,因此我希望的是可以通过传参数的方式获取不同条件下的mapper来进行数据库操作。前提是不使用if-else这种很low,而且扩展性很差,不符合开闭原则的方式。因此,我首先想到的就是策略模式,我把不同的mapper想象成不同的策略。标准的策略模式类图如下:

如果单独使用策略模式的话,实现的类图如下所示,该图是我真正实现的类图,其中IService 为mybatis-plus封装的接口,具体对数据库的操作需要继承IService,会mybatis-plus的同学自然明白,XXXServiceImpl为自己创建的对数据进行操作的实现类,即所谓的mapper,也是我们要获得的策略。

但是,单独使用策略模式的时候我发现个问题,就是获取策略的上下文类congtext,需要依赖ISerivce,即策略的父类。这个时候,我想到,是否可以结合工厂模式,通过工厂模式来获取相应的策略类,而不是通过这种上下文依赖的方式来获取策略,这样体现了工厂模式的优势,即用上下文工厂来替代传统策略模式获取策略的方式。因此,按照我的想法我做了类图的设计,对上述类图进行了改进,工厂模式+策略模式结合的类图如下:

经过上图的设计,我可以实现传入变量获取相应的数据库操作实现类,代码如下:

IService contractStrategy = ContractStrategyContextFactory.createStrategy(contractType);

工厂类中的方法如下:

//因为涉密,仅截取部分代码片段private static Map<String, ServiceImpl> hashmap = new HashMap<>();@PostConstructprivate void init(){hashmap.put(ContractMapperEnum.SERVICE_CONTRACT.getType(),serviceContractInfoService);hashmap.put(ContractMapperEnum.SPARE_PARTS_CONTRACT.getType(),sparePartsContractInfoService);}public static IService createStrategy(String type){return hashmap.get(type);}

因此,我们需求的第一步就得以实现了,通过不同接口传入的参数不同,调用不同的数据库操作类,并且没有使用任何的if else判断,降低了耦合性,并且满足了开闭原则。但是,获取到的IServie的范型是T,那么问题来了,如何根据接口参数的不同,生成相应的数据库实体对象呢,因为,BeanUtils.copyProperties来复制相应的数据库实体对象时,要先创建好实体类对象,如果问题不解决,那么我们还需要用if else来进行判断,来创建对应数据库的实体类对象。解决方式看第二部分。

二、如何根据不同条件创建相应的数据库实体对象?

首先,我知道java创建对象的方式有三种,分别是 new 、拷贝以及反射。所以,这里我第一时间想到了就是反射的方式,即 Class.forName()获取类,再用newInstance()方法创建对象,但是,这个方法返回的类中需要预先知道类的范型,不然创建的对象全部都是object,无法对数据库实体类对象进行字段的set方法。这时,我想到了继承的方式,将数据库实体类抽象出统一的父类BaseEntity,然后创建子类并用父类来作为反射的接收类,然后再进行实例化,BeanUtils.copyProperties就可以将属性copy到父类对象中,具体实现如下面的代码所示。最后将数据库实体类的父类对象,传入我们通过策略模式获取的数据库操作类,完成最后的数据库操作。此时流程全部结束,实现了我们的最终目的。

//获取数据库实体类,并用父类接收Class<ContractTempBaseEntity> clazz = (Class<ContractTempBaseEntity>)Class.forName(className);//实例化ContractTempBaseEntity contractTempBaseEntity = clazz.newInstance();//将接口参数copy到数据库实体类对象BeanUtils.copyProperties(baseContractTempInfoDTO,contractTempBaseEntity);//将对象传入到通过策略模式获取的数据库操作类,完成最后的数据库操作contractStrategy.saveOrUpdate(contractTempBaseEntity);

以上涉及到的枚举类如下:

//枚举类例子SPARE_PARTS_CONTRACT("SparePartsContractInfo", "数据库实体类的类路径,作为class.forName的参数"),SPARE_PARTS_CONTRACT_TEMP("SparePartsContractTempInfo","数据库实体类的类路径,作为class.forName的参数"),

总结

通过以上方式,基本上完成了需求的痛点,而且可以满足日后的扩展,最终从controller 到service再到Dao层的时序图如下:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。