目录
- 一、场景举例
- 二、什么时候需要改造 if-else?
- 三、策略模式 + Map字典
- 3.1 策略接口
- 3.2 策略实现类
- 3.3 策略工厂类(策略接口的持有者)
- 3.4 客户端(测试类)
- 3.5 执行结果
- 3.6 总结
- 四、责任链模式
- 4.1 责任链处理接口
- 4.2 责任链处理实现类
- 4.3 责任链容器类(责任链处理接口持有者)
- 4.4 客户端(测试类)
- 4.5 执行结果
- 4.6 总结
- 五、补充:反射工具类
一、场景举例
假设有一个电商平台,再结算的时候需要根据不同的商品类型来计算优惠价格。平台定义了几种商品类型:A
、B
、C
、D
。每种商品类型享受不同的优惠活动,需要根据商品类型来进行相应的计算。
if-else 代码实现:
/*** 处理订单* @param type 订单类型*/
public void handle(String type) {if (Objects.equals("normal", type)) {// 普通商品System.out.println("收到`普通商品`订单");System.out.println("开始解析`普通商品`订单内容");System.out.println("执行`普通商品`订单业务逻辑");System.out.println("`普通商品`订单处理完成");} else if (Objects.equals("discount", type)) {// 打折商品System.out.println("收到`打折商品`订单");System.out.println("开始解析`打折商品`订单内容");System.out.println("执行`打折商品`订单业务逻辑");System.out.println("`打折商品`订单处理完成");} else if (Objects.equals("fullReduction", type)) {// 满减商品System.out.println("收到`满减商品`订单");System.out.println("开始解析`满减商品`订单内容");System.out.println("执行`满减商品`订单业务逻辑");System.out.println("`满减商品`订单处理完成");} else if (Objects.equals("groupPurchase", type)) {// 团购商品System.out.println("收到`团购商品`订单");System.out.println("开始解析`团购商品`订单内容");System.out.println("执行`团购商品`订单业务逻辑");System.out.println("`团购商品`订单处理完成");}// ......未来可能还有好多个 else if
}
就上面例子,当订单类型越来越多时,分支 else if
就会越来越多,每增加一个订单类型,就需要修改或添加 if-else 分支,违反了开闭原则(对扩展开放,对修改关闭)。
二、什么时候需要改造 if-else?
这里引用阿里规约对于什么时候改造 if-else 的一段话:
当
if-else
的代码违反了单一职责原则、开闭原则,尤其是块中的代码量较大时,后续代码的扩展和维护就会变得非常困难且容易出错,使用卫语句
也同样避免不了这两个问题。
因此根据经验,得出了一个我个人认为比较好的实践:
if-else 不超过 2 层,块中代码 1~5 行
:直接写到 if-else 块中;if-else 超过 2 层,块中代码不超过 3 行
:尽量使用卫语句(封装为方法);if-else 超过 2 层,块中代码超过 3 行
:尽量使用状态模式、策略模式。
PS:卫语句如下所示:
public handle(String type) {if (value == 1) {return true;}if (value == 2) {return true;}return false;
}
现在我们已经了解到什么场景下会需要改造 if-else 分支结构,那有哪些设计模式可以用来改造 if-else 分支结构呢?
三、策略模式 + Map字典
策略模式: 封装一系列的算法,它们具有共性,可以相互替换,也就是说让算法独立于使用它的客户端而独立变化,客户端仅仅依赖于策略接口。
在上述根据订单类型处理订单场景中,我们可以把 if-else 分支的业务逻辑抽取为各种策略,但是不可避免的是依然需要客户端写一些 if-else 进行策略选择的逻辑,我们可以将这段逻辑抽取到工厂类中去,这就是 策略模式
+简单工厂
,代码如下:
包结构如下:
3.1 策略接口
public interface IOrderHandlerStrategy {/*** 处理订单*/void handleOrder();
}
3.2 策略实现类
public class NormalOrderHandlerStrategy implements IOrderHandlerStrategy {@Overridepublic void handleOrder() {System.out.println("收到`普通商品`订单");System.out.println("开始解析`普通商品`订单内容");System.out.println("执行`普通商品`订单业务逻辑");System.out.println("`普通商品`订单处理完成");}
}public class DiscountOrderHandlerStrategy implements IOrderHandlerStrategy {@Overridepublic void handleOrder() {System.out.println("收到`打折商品`订单");System.out.println("开始解析`打折商品`订单内容");System.out.println("执行`打折商品`订单业务逻辑");System.out.println("`打折商品`订单处理完成");}
}public class FullReductionOrderHandlerStrategy implements IOrderHandlerStrategy {@Overridepublic void handleOrder() {System.out.println("收到`满减商品`订单");System.out.println("开始解析`满减商品`订单内容");System.out.println("执行`满减商品`订单业务逻辑");System.out.println("`满减商品`订单处理完成");}
}public class GroupPurchaseOrderHandlerStrategy implements IOrderHandlerStrategy {@Overridepublic void handleOrder() {System.out.println("收到`团购商品`订单");System.out.println("开始解析`团购商品`订单内容");System.out.println("执行`团购商品`订单业务逻辑");System.out.println("`团购商品`订单处理完成");}
}
3.3 策略工厂类(策略接口的持有者)
import java.util.HashMap;
import java.util.Map;/*** <p> @Title OrderHandlerStrategyContext* <p> @Description TODO** @author zhj* @date 2023/10/12 17:56*/
public class OrderHandlerStrategyFactory {/*** 订单处理策略*/private Map<String, IOrderHandlerStrategy> orderHandlerStrategyMap;/*** 构造方法,初始化策略*/public OrderHandlerStrategyFactory() {this.orderHandlerStrategyMap = new HashMap<>();orderHandlerStrategyMap.put("normal", new NormalOrderHandlerStrategy());orderHandlerStrategyMap.put("discount", new DiscountOrderHandlerStrategy());orderHandlerStrategyMap.put("groupPurchase", new GroupPurchaseOrderHandlerStrategy());orderHandlerStrategyMap.put("fullReduction", new FullReductionOrderHandlerStrategy());}/*** 获取订单处理策略** @param type 订单类型* @return 订单处理策略*/public IOrderHandlerStrategy getOrderHandlerStrategy(String type) {return orderHandlerStrategyMap.get(type);}
}
3.4 客户端(测试类)
public class Client {/*** 处理订单* @param type 订单类型*/public void handleWithStrategy(String type) {// 获取订单处理策略IOrderHandlerStrategy orderHandlerStrategy = new OrderHandlerStrategyFactory().getOrderHandlerStrategy(type);// 执行订单处理策略orderHandlerStrategy.handleOrder();}public static void main(String[] args) {Client client = new Client();client.handleWithStrategy("normal");}
}
3.5 执行结果
3.6 总结
- 经过对
策略模式
+简单工厂
方案的改造,我们已经消除了 if-else 的结构,每当 新加入一种处理方式,只需要 添加新的策略实现类,并修改工厂中的 Map 集合 即可。 - 如果要使得程序符合开闭原则,则需要调整工厂类种处理策略的获取方式,通过
反射
或Spring 注入
的方式,获取指定包下的所有策略实现类,然后放到字典 Map 中。
四、责任链模式
责任链模式: 是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下一个节点的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
发出请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
包结构如下:
4.1 责任链处理接口
public interface IOrderHandler {/*** 处理订单*/void handleOrder();
}
4.2 责任链处理实现类
public class NormalOrderHandler implements IOrderHandler {@Overridepublic void handleOrder(String orderType, OrderHandlerChain chain) {// 普通商品if ("normal".equals(orderType)) {System.out.println("收到`普通商品`订单");System.out.println("开始解析`普通商品`订单内容");System.out.println("执行`普通商品`订单业务逻辑");System.out.println("`普通商品`订单处理完成");} else {chain.handleOrder(orderType);}}
}public class DiscountOrderHandler implements IOrderHandler {@Overridepublic void handleOrder(String orderType, OrderHandlerChain chain) {// 打折商品if ("discount".equals(orderType)) {System.out.println("收到`打折商品`订单");System.out.println("开始解析`打折商品`订单内容");System.out.println("执行`打折商品`订单业务逻辑");System.out.println("`打折商品`订单处理完成");} else {chain.handleOrder(orderType);}}
}public class GroupPurchaseOrderHandler implements IOrderHandler {@Overridepublic void handleOrder(String orderType, OrderHandlerChain chain) {// 团购商品if ("groupPurchase".equals(orderType)) {System.out.println("收到`团购商品`订单");System.out.println("开始解析`团购商品`订单内容");System.out.println("执行`团购商品`订单业务逻辑");System.out.println("`团购商品`订单处理完成");} else {chain.handleOrder(orderType);}}
}public class FullReductionOrderHandler implements IOrderHandler {@Overridepublic void handleOrder(String orderType, OrderHandlerChain chain) {// 满减商品if ("fullReduction".equals(orderType)) {System.out.println("收到`满减商品`订单");System.out.println("开始解析`满减商品`订单内容");System.out.println("执行`满减商品`订单业务逻辑");System.out.println("`满减商品`订单处理完成");} else {chain.handleOrder(orderType);}}
}
4.3 责任链容器类(责任链处理接口持有者)
import com.demo.test.handler.IOrderHandler;import java.util.ArrayList;
import java.util.List;public class OrderHandlerChain {/*** 订单处理器列表*/private List<IOrderHandler> orderHandlers = new ArrayList<>();/*** 订单处理器索引*/private ThreadLocal<Integer> index = ThreadLocal.withInitial(() -> 0);/*** 添加订单处理器** @param orderHandler 订单处理器*/public OrderHandlerChain addHandler(IOrderHandler orderHandler) {orderHandlers.add(orderHandler);return this;}/*** 执行订单处理器链** @param orderType 订单类型*/public void handleOrder(String orderType) {if (index.get() >= orderHandlers.size()) {// 重置索引index.set(0);return;}// 处理订单IOrderHandler orderHandler = orderHandlers.get(index.get());orderHandler.handleOrder(orderType, this);index.set(index.get() + 1);}
}
4.4 客户端(测试类)
public class Client {public static void main(String[] args) {OrderHandlerChain chain = new OrderHandlerChain();// 添加订单处理责任链chain.addHandler(new NormalOrderHandler()).addHandler(new DiscountOrderHandler()).addHandler(new GroupPurchaseOrderHandler()).addHandler(new FullReductionOrderHandler());chain.handleOrder("normal");}
}
4.5 执行结果
4.6 总结
- 通过
责任链
的处理方式,if-else 结构也被消除了。每当 新加入一种处理方式,只需要 添加责任链处理接口的实现类,并修改责任链容器类 即可。 - 如果要使得程序符合开闭原则,则需要调整责任链处理接口实现类的获取方式,通过
反射
或Spring 注入
的方式,获取指定包下的所有实现类。
五、补充:反射工具类
这里补充一个反射工具类,用于获取 指定包 下 某个接口 的 所有实现类。
/** * @Description: 反射工具类 * @Auther: wuzhazha */
public class ReflectionUtil { /** * 定义类集合(用于存放所有加载的类) */ private static final Set<Class<?>> CLASS_SET; static { //指定加载包路径 CLASS_SET = getClassSet("com.yaolong"); } /** * 获取类加载器 * @return */ public static ClassLoader getClassLoader(){ return Thread.currentThread().getContextClassLoader(); } /** * 加载类 * @param className 类全限定名称 * @param isInitialized 是否在加载完成后执行静态代码块 * @return */ public static Class<?> loadClass(String className,boolean isInitialized) { Class<?> cls; try { cls = Class.forName(className,isInitialized,getClassLoader()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return cls; } public static Class<?> loadClass(String className) { return loadClass(className,true); } /** * 获取指定包下所有类 * @param packageName * @return */ public static Set<Class<?>> getClassSet(String packageName) { Set<Class<?>> classSet = new HashSet<>(); try { Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".","/")); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals("file")) { String packagePath = url.getPath().replace("%20",""); addClass(classSet,packagePath,packageName); } else if (protocol.equals("jar")) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); if (jarURLConnection != null) { JarFile jarFile = jarURLConnection.getJarFile(); if (jarFile != null) { Enumeration<JarEntry> jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(".class")) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); doAddClass(classSet,className); } } } } } } } } catch (IOException e) { throw new RuntimeException(e); } return classSet; } private static void doAddClass(Set<Class<?>> classSet, String className) { Class<?> cls = loadClass(className,false); classSet.add(cls); } private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) { final File[] files = new File(packagePath).listFiles(new FileFilter() { @Override public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); } }); for (File file : files) { String fileName = file.getName(); if (file.isFile()) { String className = fileName.substring(0, fileName.lastIndexOf(".")); if (StringUtils.isNotEmpty(packageName)) { className = packageName + "." + className; } doAddClass(classSet,className); } else { String subPackagePath = fileName; if (StringUtils.isNotEmpty(packagePath)) { subPackagePath = packagePath + "/" + subPackagePath; } String subPackageName = fileName; if (StringUtils.isNotEmpty(packageName)) { subPackageName = packageName + "." + subPackageName; } addClass(classSet,subPackagePath,subPackageName); } } } public static Set<Class<?>> getClassSet() { return CLASS_SET; } /** * 获取应用包名下某父类(或接口)的所有子类(或实现类) * @param superClass * @return */ public static Set<Class<?>> getClassSetBySuper(Class<?> superClass) { Set<Class<?>> classSet = new HashSet<>(); for (Class<?> cls : CLASS_SET) { if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) { classSet.add(cls); } } return classSet; } /** * 获取应用包名下带有某注解的类 * @param annotationClass * @return */ public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass) { Set<Class<?>> classSet = new HashSet<>(); for (Class<?> cls : CLASS_SET) { if (cls.isAnnotationPresent(annotationClass)) { classSet.add(cls); } } return classSet; } }
整理完毕,完结撒花~ 🌻
参考地址:
1.用设计模式干掉 if-else,太优雅了!https://mp.weixin.qq.com/s/LhfpxqiJZMEcnKDiOTwspg
2.策略模式:精妙替换你的if-else,https://zhuanlan.zhihu.com/p/453689771?utm_id=0