如何用策略模式,优化你代码里的的if-else?
创始人
2024-03-29 22:52:03
0

最近有一个学妹在跟我沟通如何有效的去避免代码中一长串的if else判断或者switch条件判断?针对更多的回答就是合理的去使用设计来规避这个问题。

在设计模式中,可以使用工厂模式或者策略模式来处理这类问题,之前已经分享了工厂模式,感兴趣的同学可以去复习一下。

设计模式系列往期文章:

  • 单例模式
  • 工厂模式
  • 流程引擎
  • 建造者模式
  • 原型模式
  • 责任链模式
  • 观察者模式

那么工厂模式和策略模式有什么区别呢?

  • 工厂模式是属于创建型设计模式,主要用来针对不同类型创建不同的对象,达到解偶类对象。
  • 策略模式是属于行为型设计模式,主要是针对不同的策略做出对应行为,达到行为解偶

本次就来具体聊聊策略模式它是如何做到行为解耦

大纲

定义

什么是策略模式?它的原理实现是怎么样的?

定义一系列算法,封装每个算法,并使他们可以互换,不同的策略可以让算法独立于使用它们的客户而变化。 以上定义来自设计模式之美

感觉有点抽象?那就来看一张结构图吧

  • Strategy(抽象策略):抽象策略类,并且定义策略执行入口
  • ConcreteStrategy(具体策略):实现抽象策略,实现algorithm方法
  • Context(环境):运行特定的策略类。

这么看结构其实还是不复杂的,而且跟状态模式类似。

那么这个代码怎么实现?

举个例子,汽车大家肯定都不陌生,愿大家早日完成汽车梦,汽车的不同档(concreteStrategy)就好比不同的策略,驾驶者选择几档则汽车按几档的速度前进,整个选择权在驾驶者(context)手中。

public interface GearStrategy {// 定义策略执行方法void algorithm(String param);
}
复制代码

首先还是先定义抽象策略

这里是用接口的形式,还有一种方式可以用抽象方法abstract来写也是一样的。具体就看大家自己选择了。

public abstract class GearStrategyAbstract {// 定义策略执行方法abstract void algorithm(String param);
}
复制代码
public class GearStrategyOne implements GearStrategy {@Overridepublic void algorithm(String param) {System.out.println("当前档位" + param);}
}
复制代码

其次定义具体档位策略,实现algorithm方法。

public class Context {// 缓存所有的策略,当前是无状态的,可以共享策略类对象private static final Map strategies = new HashMap<>();// 第一种写法static {strategies.put("one", new GearStrategyOne());}public static GearStrategy getStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}return strategies.get(type);}// 第二种写法public static GearStrategy getStrategySecond(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}if (type.equals("one")) {return new GearStrategyOne();}return null;}public static void main(String[] args) {// 测试结果GearStrategy strategyOne = Context.getStrategy("one");strategyOne.algorithm("1档");// 结果:当前档位1档GearStrategy strategyTwo = Context.getStrategySecond("one");strategyTwo.algorithm("1档");// 结果:当前档位1档}}
复制代码

最后就是实现运行时环境(Context),你可以定义成StrategyFactory,但都是一个意思。

在main方法里面的测试demo,可以看到通过不同的type类型,可以实现不同的策略,这就是策略模式主要思想。

在Context里面定义了两种写法:

  • 第一种是维护了一个strategies的Map容器。用这种方式就需要判断每种策略是否可以共享使用,它只是作为算法的实现。
  • 第二种是直接通过有状态的类,每次根据类型new一个新的策略类对象。这个就需要根据实际业务场景去做的判断。

框架的应用

策略模式在框架中也在一个很常见的地方体现出来了,而且大家肯定都有使用过。

那就是JDK中的线程池ThreadPoolExecutor

首先都是类似于这样定义一个线程池,里面实现线程池的异常策略。

这个线程池的异常策略就是用的策略模式的思想。

在源码中有RejectedExecutionHandler这个抽象异常策略接口,同时它也有四种拒绝策略。关系图如下:

这就是在框架中的体现了,根据自己的业务场景,合理的选择线程池的异常策略。

业务改造举例

在真实的业务场景中策略模式也还是应用很多的。

在社交电商中分享商品是一个很重要的环节,假设现在要我们实现一个分享图片功能,比如当前有 单商品、多商品、下单、会场、邀请、小程序链接等等多种分享场景。

针对上线这个流程图先用if else语句做一个普通业务代码判断,就像下面的这中方式:

public class SingleItemShare {// 单商品public void algorithm(String param) {System.out.println("当前分享图片是" + param);}
}
public class MultiItemShare {// 多商品public void algorithm(String param) {System.out.println("当前分享图片是" + param);}
}
public class OrderItemShare {// 下单public void algorithm(String param) {System.out.println("当前分享图片是" + param);}
}
public class ShareFactory {public static void main(String[] args) throws Exception {Integer shareType = 1;// 测试业务逻辑if (shareType.equals(ShareType.SINGLE.getCode())) {SingleItemShare singleItemShare = new SingleItemShare();singleItemShare.algorithm("单商品");} else if (shareType.equals(ShareType.MULTI.getCode())) {MultiItemShare multiItemShare = new MultiItemShare();multiItemShare.algorithm("多商品");} else if (shareType.equals(ShareType.ORDER.getCode())) {OrderItemShare orderItemShare = new OrderItemShare();orderItemShare.algorithm("下单");} else {throw new Exception("未知分享类型");}// .....省略更多分享场景}enum ShareType {SINGLE(1, "单商品"),MULTI(2, "多商品"),ORDER(3, "下单");/*** 场景对应的编码*/private Integer code;/*** 业务场景描述*/private String desc;ShareType(Integer code, String desc) {this.code = code;this.desc = desc;}public Integer getCode() {return code;}// 省略 get set 方法}
}
复制代码

这里大家可以看到每新加一种分享类型,就需要加一次if else 判断,当如果有十几种场景的时候那代码整体就会非常的长,看起来给人的感觉也不是很舒服。

接下来就看看如何用策略模式进行重构:

public interface ShareStrategy {// 定义分享策略执行方法void shareAlgorithm(String param);
}public class OrderItemShare implements ShareStrategy {@Overridepublic void shareAlgorithm(String param) {System.out.println("当前分享图片是" + param);}
}// 省略 MultiItemShare以及SingleItemShare策略// 分享工厂
public class ShareFactory {// 定义策略枚举enum ShareType {SINGLE("single", "单商品"),MULTI("multi", "多商品"),ORDER("order", "下单");// 场景对应的编码private String code;// 业务场景描述private String desc;ShareType(String code, String desc) {this.code = code;this.desc = desc;}public String getCode() {return code;}// 省略 get set 方法}// 定义策略map缓存private static final Map shareStrategies = new       HashMap<>();static {shareStrategies.put("order", new OrderItemShare());shareStrategies.put("single", new SingleItemShare());shareStrategies.put("multi", new MultiItemShare());}// 获取指定策略public static ShareStrategy getShareStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}return shareStrategies.get(type);}public static void main(String[] args) {// 测试demoString shareType = "order";ShareStrategy shareStrategy = ShareFactory.getShareStrategy(shareType);shareStrategy.shareAlgorithm("order");// 输出结果:当前分享图片是order}
}复制代码

这里策略模式就已经改造完了。在client请求端,根本看不到那么多的if else判断,只需要传入对应的策略方式即可,这里我们维护了一个策略缓存map,在直接调用的ShareFactory获取策略的时候就直接是从换种获取策略类对象。

这就已经达到了行为解偶的思想。同时也避免了长串的if else 判断。

优点:

  • 算法策略可以自由实现切换
  • 扩展性好,加一个策略,只需要增加一个类

缺点:

  • 策略类数量多
  • 需要维护一个策略枚举,让别人知道你当前具有哪些策略

总结

以上就讲完了策略模式,整体看上去其实还是比较简单的,还是那句话学习设计模式我们还是要学习每种设计模式的思想,任何一种设计模式存在即合理。当然也不要因为设计模式而设计代码,那样反而得不偿失。

我是敖丙,你知道的越多,你不知道的越多,感谢各位人才的:点赞收藏评论,我们下期见!


文章持续更新,可以微信搜一搜「 三太子敖丙 」第一时间阅读,回复【资料】有我准备的一线大厂面试资料和简历模板,本文 GitHub github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...