干掉了传统if-else的竟然是它!太长见识了![策略模式的具体实现]
普通的策略模式见文章:https://www.cnblogs.com/java-spring/p/12988778.html
下面是更加具体的特殊的策略模式的实现!
由于项目的需求,需要对接多家招采平台和多家保险公司,每次支付完成后,都要根据选择的不同的保险公司,做特殊的逻辑处理。一段时间就会增加一个来源,已经把之前的逻辑改的面目全。出于长远的考虑,我决定对现有的逻辑进行重构,毕竟长痛不如短痛。
传统的实现方式
我们看下边的伪代码,大致就是重构前逻辑的代码,由于来源比较少,简单的做if-else逻辑判断足以满足需求。
但每过一段时间,来源说不定就会增加,再用这种方式做已经无法维护了,想象一下那种臃肿的if-else代码,别说开发想想都头大!
public class OrderServiceImpl implements IOrderService {
@Override
public String handle(String companyId) {
if ("A".equals(companyId)) {
return "进行A保险公司投保业务";
} else if ("B".equals(companyId)) {
return "进行B保险公司投保业务";
} else if ("C".equals(companyId)) {
return "进行C保险公司投保业务";
}
return null;
}
}
策略模式的实现方式
策略模式定义了一个拥有共同行为的算法族,每个算法都被封装起来,可以互相替换,独立于客户端而变化。
一、策略模式的使用场景:
针对同一问题的多种处理方式,仅仅是具体行为有差别时;需要安全地封装多种同一类型的操作时;同一抽象类有多个子类,而客户端需要使用if-else 或者 switch-case 来选择具体子类时。
每个保险公司来源都有自己单独的逻辑实现类,而每次需要添加保险公司来源,直接新建实现类,修改@HandlerType(“zhlh”)的数值即可,再也不用去翻又臭又长的if-lese。
不仅如此在分配任务时,每个人负责开发几种订单来源逻辑,都可以做到互不干扰,而且很大程度上减少了合并代码的冲突。
二、具体的实现过程:
自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandlerType {
String value() default "";
}
自启动包扫描类
@Component
public class MyHandler implements ApplicationRunner {
//扫描到的类的容器
private List<String> classNames=new ArrayList<String>();
//存放扫描到被@HandlerType注解上的实现类
private Map<String,Class> maps=new HashMap<>();
@Override
public void run(ApplicationArguments args) throws Exception {
scanPackage("com.ftx.jwt");
System.out.println(classNames);
for(String className:classNames){
//拿到类的全路径名,去掉后缀名
String cn = className.replace(".class", "");
Class<?> clazz = Class.forName(cn);
if(clazz.isAnnotationPresent(HandlerType.class)){
System.out.println(clazz.getName()+"实现类已被@HandlerType注解标注");
HandlerType annotation = (HandlerType)clazz.getAnnotation(HandlerType.class);
maps.put(annotation.value(),clazz);
}
}
//注册到spring容器
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
defaultListableBeanFactory.registerSingleton("BeanName",new HandlerContext(maps));
}
//扫描包
public void scanPackage(String packageName){
URL url = this.getClass().getClassLoader().getResource("" + packageName.replaceAll("\\.", "/"));
System.out.println(url);
// file:/D:/IdeaProjects/springboot-myannotation/target/classes/com/ftx/myannotation
String file = url.getFile();
File files=new File(file);
for(File file1:files.listFiles()){
if(file1.isDirectory()){
//递归扫描
scanPackage(packageName+"."+file1.getName());
}else{
classNames.add(packageName+"."+file1.getName());
//包名+类名 com.ftx.myannotation.controller.TestController
}
}
}
}
存储实现类的容器类
此容器存储项目启动时把扫描到的加注解的实现类,并加入到spring容器中,根据类型type实例化抽象类
public class HandlerContext {
private static Map<String, Class> handlerMap;
public HandlerContext(Map<String, Class> handlerMap) {
this.handlerMap = handlerMap;
}
public static AbstractHandler getInstance(HttpServletRequest request,String type) throws IllegalAccessException, InstantiationException {
ServletContext servletContext = request.getSession().getServletContext();
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Class clazz = handlerMap.get(type);
if (clazz == null) {
throw new IllegalArgumentException("没有找到@HandlerType的value值为 " + type+" 的实现类");
}
AbstractHandler abstractHandler = (AbstractHandler) clazz.newInstance();
return abstractHandler;
}
}
向上抽象出来一个具体的业务处理器
public abstract class AbstractHandler {
/**
* 功能描述:投保
*/
abstract public String assemblyData();
}
实现抽象类的两个实现类
@HandlerType(value = "one")
public class AbstractHandlerImpl1 extends AbstractHandler {
@Override
public String assemblyData() {
return "我是标注为 one 的实现类的方法返回值";
}
}
@HandlerType(value = "two")
public class AbstractHandlerImpl2 extends AbstractHandler {
@Override
public String assemblyData() {
return "我是标注为 two 的实现类的方法返回值";
}
}
调用入口
@GetMapping("/test")
public void test(HttpServletRequest request,String type) throws InstantiationException, IllegalAccessException {
AbstractHandler one = HandlerContext.getInstance(request, type);
String s = one.assemblyData();
System.out.println("执行的是被@HandlerType标注且value值为"+ type +"的实现类的assemblyData方法");
System.out.println(s);
}
以上设计模式方式看着略显复杂,很些小伙伴提出质疑:“你为了个if-else,弄的如此的麻烦,又是自定义注解,又弄这么多类不麻烦吗?” 还有一些小伙伴纠结于性能问题,策略模式的性能可能确实不如if-else。
但我觉得吧增加一点复杂度、牺牲一丢丢性能,换代码的整洁和可维护性还是值得的。不过,一个人一个想法,怎么选还是看具体业务场景吧!
策略模式的优缺点
优点
易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合开放封闭原则避免使用多重条件选择语句,充分体现面向对象设计思想 策略类之间可以自由切换,由于策略类都实现同一个接口,所以使它们之间可以自由切换每个策略类使用一个策略类,符合单一职责原则 客户端与策略算法解耦,两者都依赖于抽象策略接口,符合依赖反转原则客户端不需要知道都有哪些策略类,符合最小知识原则
缺点
策略模式,当策略算法太多时,会造成很多的策略类客户端不知道有哪些策略类,不能决定使用哪个策略类,这点可以通过封装common公共包解决,也可以考虑使IOC容器和依赖注入的方式来解决。
总结
凡事都有他的两面性,if-else多层嵌套和也都有其各自的优缺点:
if-else的优点就是简单,想快速迭代功能,逻辑嵌套少且不会持续增加,if-else更好些,缺点也是显而易见,代码臃肿繁琐不便于维护。
策略模式 将各个场景的逻辑剥离出来维护,同一抽象类有多个子类,需要使用if-else 或者 switch-case 来选择具体子类时,建议选策略模式,他的缺点就是会产生比较多的策略类文件。
两种实现方式各有利弊,如何选择还是要依据具体业务场景,还是那句话设计模式不是为了用而用,一定要用在最合适的位置。
注:以上代码只是策略模式的一种实现方法,策略模式属于设计模式的一种,设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!

浙公网安备 33010602011771号