Java_23种设计模式之策略模式——一蓑烟雨任平生
作者:互联网
文章目录
前言
这次我们来讲解一下策略模式,策略模式是我们日常开发天天都在用的“模式”,最简单if/else就是策略,而我们用不同的策略(分支)来实现结果的区分。所以策略模式是非常重要的模式,也是理解和应用最为简单的方式(大概)。
这里再次提醒:不要过分拘泥于设计模式的类和形式,只要记住一点:将变与不变抽离的过程就是设计模式
一、什么是策略模式?
策略模式按照最简单的理解就是对if/else的解耦,也是他最常用的场景,最典型的应用场景就是购物的时候,选择用优惠券,还是满2件送一件,或者凑够多少金额满减等等,按照一般的写法,我们经常会写出大量的if/else,在代码量较少的时候,这种写的方式既简单又方便,但是一旦代码复杂,复杂的if/else会让代码越来越屎,策略模式也是为了解决此问题而产生的。
策略模式是一种行为型模式,他将一类相似的行为解耦,并且将策略封装到具体的策略实现类。
策略模式结构图:
什么情况下使用策略模式?
- 当代码充斥大量if/else并且他们只是行为不同的时候,建议使用
- 将复杂的策略内容封装到单独的类情况下,比如我们的策略内容需要进行非常复杂的计算
策略模式的特点:
- 将相似的行为进行封包,客户端指定策略已达到不同行为的切换
- 将复杂的业务实现逻辑代码封装到单独的策略,可以通过context组合使用策略
工厂模式和策略模式的异同:
相同点:
- 策略的"执行对象"和工厂生产的“抽象对象”,他们都具有相似的行为。
- 都是为了抽离过程和结果实现本身。
不同点:
- 工厂模式是为了创建对象,而策略是为了解决复杂的if/else嵌套
- 工厂模式只需要传递工厂需要的参数,而策略模式则需要具体的实现类支撑。
- 工厂模式是创建型设计模式,而策略模式是行为型模式。前者专注于对象的创建过程,后者专注于对象的具体行为
如果上面不够清晰,那么下面我给出一个具体一些的案例来说说他们的区别:
我们都知道低价手机的生产基本都是找代工厂,而代工厂可能不止生产一个品牌的手机,他可能承接多个品牌的手机生产,经销商让工厂生产指定的手机,而工厂负责手机的“创建”,这一模式就是典型的 工厂模式,而工厂根据不同的手机品牌,投入不同的生产材料和生成力,这个抉择的过程就是策略模式
场景模拟:
一些交易的系统,在遇到特殊情况的时候,需要进行网络监控或者管理,有时候需要根据某种条件下触发监控或者报警,比如网关接受一笔交易,需要根据交易的校验情况,在不同的校验代码段进行钉钉机器人报警,下面给出几种情况:
- 查不出必要数据的时候,给出对应的告警。提醒运营人员排查线上环境
- 当数据量到达指定的限制量的时候,给出风险告警。
- 当出现黑名单人员进行交易拦截的时候,进行日志记录,不进行警告
不使用设计模式:
/**
* 策略模式:
* 不使用设计模式实现告警
* * home.php?mod=space&uid=686208 zxd
* home.php?mod=space&uid=1248337 1.0
* home.php?mod=space&uid=686237 2021/1/26 23:41
*/
public class Main {
/**
* 不使用模式
*/
public static void main(String[] args) {
System.out.println("接受交易");
service1();
service2();
service3();
System.out.println("完成交易");
}
/**
* 模拟触发了业务场景1
* 出现机房断电或者查不出必要数据的时候,给出对应的告警。提醒运营人员排查线上环境
*/
private static void service1() {
// 为了模拟异常情况,我们用 1/0 触发一个异常
try {
// 程序到了这一步算不下去了
int result = 1/0;
System.out.println("具体的业务");
} catch (Exception e) {
System.err.println("警告,服务器出现异常");
System.out.println("开始执行报警");
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.err.println("执行报警完成");
throw e;
}
}
/**
* 模拟触发了业务场景2
* 当数据量到达指定的限制量的时候,给出风险告警。
*/
private static void service2() {
int limit = 1000;
int count = 2000;
if(count > limit){
System.out.println("开始执行报警");
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
//.....
// logger.info("警告,当前数据请求量达到限制值")
System.err.println("执行报警完成");
}
}
/**
* 模拟触发了业务场景3
* 当出现黑名单人员进行交易拦截的时候,进行日志记录,不进行警告
*/
private static void service3() {
boolean flag = true;
if(flag){
// 触犯黑名单:
// logger.info("警告,当前请求");
// 提前退出,结束交易
return;
}
System.out.println("正常完成下面的步骤");
}
}
上面的代码存在如下的问题:
-
当我们需要新增一处监控的时候,需要在对应的代码块增加监控和报警的逻辑
-
所有的改动都在一处,如果代码内容复杂会造成业务逻辑混淆
-
当告警的业务日趋复杂,告警的代码将变得难以维护
使用工厂模式:
生产策略:
public class StrategyFactory {
/**
* 创建策略
* home.php?mod=space&uid=952169 service
* @return
*/
public CaveatStrategy createStrategy(String service){
// 数量监控
if(Objects.equals(service, "quantity")){
return new QuantityStrategy();
}else if(Objects.equals(service, "noresult")){
// 没有返回值
return new NoResultStrategy();
}else if(Objects.equals(service, "blacklist")){
// 黑名单
return new BlackListStrategy();
}
return null;
}
}
黑名单策略类:
public class BlackListStrategy implements CaveatStrategy {
@Override
public void warning(Map<String, Object> params) {
boolean flag = Boolean.parseBoolean(params.get("flag").toString());
if (flag) {
System.err.println("触犯黑名单列表,但不警告");
}
}
}
数量监控策略类:
public class QuantityStrategy implements CaveatStrategy {
@Override
public void warning(Map<String, Object> params) {
int limit = Integer.parseInt(params.get("limit").toString());
int count = Integer.parseInt(params.get("count").toString());
if(count > limit){
System.err.println("警告,当前数据内容无法获取返回值");
}
}
}
单元测试:
public class Main {
/**
*
* @param args
*/
public static void main(String[] args) {
// 模拟交易流转参数对象
Map<String, Object> objectObjectHashMap = new HashMap<>();
StrategyFactory strategyFactory = new StrategyFactory();
CaveatStrategy strategy = strategyFactory.createStrategy("quantity");
// 表示除数和被除数
objectObjectHashMap.put("limit", "1000");
objectObjectHashMap.put("count", "2000");
strategy.warning(objectObjectHashMap);
strategy = strategyFactory.createStrategy("noresult");
objectObjectHashMap.put("divisor", "1");
objectObjectHashMap.put("dividend", "0");
strategy.warning(objectObjectHashMap);
strategy = strategyFactory.createStrategy("blacklist");
objectObjectHashMap.put("flag", true);
strategy.warning(objectObjectHashMap);
}/*结果如下:
警告,当前数据内容无法获取返回值
触犯黑名单列表,但不警告
*/
}
上面的代码存在如下的问题:
- 策略工厂虽然解决了策略的生产问题,但是需要自己指定策略,而且每次更换策略内容会导致工厂的代码也需要随之改动
- 维护和扩展都需要依赖工厂,我们每多一个策略都需要更换工厂的内容
- 当告警的业务日趋复杂,工厂的代码将会越发的臃肿
策略模式:
策略类的上下文:
public class StrategyContext {
private CaveatStrategy strategy;
public StrategyContext(CaveatStrategy strategy) {
this.strategy = strategy;
}
public void doStrategy(Map<String, Object> params){
strategy.warning(params);
}
}
策略模式的单元测试:
public class Main {
/**
* 使用策略模式
* @param args
*/
public static void main(String[] args) {
Map<String, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("limit", "1000");
objectObjectHashMap.put("count", "2000");
objectObjectHashMap.put("divisor", "1");
objectObjectHashMap.put("dividend", "0");
objectObjectHashMap.put("flag", true);
CaveatStrategy blackListStrategy = new BlackListStrategy();
CaveatStrategy noResultStrategy = new NoResultStrategy();
CaveatStrategy quantityStrategy = new QuantityStrategy();
// 三种策略独立
StrategyContext strategyContext4 = new StrategyContext(blackListStrategy);
strategyContext4.doStrategy(objectObjectHashMap);
strategyContext4 = new StrategyContext(noResultStrategy);
strategyContext4.doStrategy(objectObjectHashMap);
strategyContext4 = new StrategyContext(quantityStrategy);
strategyContext4.doStrategy(objectObjectHashMap);
}/*
触犯黑名单列表,但不警告
警告,当前数据内容无法获取返回值
触犯黑名单列表,但不警告
警告,当前数据内容无法获取返回值
*/
}
从上面的内容可以看出,我们只需要把策略传给上下文,上下文会根据传入的策略自动匹配对应的策略执行报警。
但是我们也发现了一些问题:
- 代码存在new策略类,这又回到以前不使用工厂的时候情况了
- 如果我们用策略组合,虽然少了很多的if/else,但是建立策略的细节依旧在客户端。
简单工厂+策略模式:
public class StrategyContext {
private CaveatStrategyFactory caveatStrategyFactory = new CaveatStrategyFactory();
public void doStrategy(String service, Map<String, Object> params){
caveatStrategyFactory.createStrategy(service).warning(params);
}
}
public class CaveatStrategyFactory {
/**
* 创建策略
* @param service 策略
*/
public CaveatStrategy createStrategy(String service){
// 数量监控
if(Objects.equals(service, "quantity")){
return new QuantityStrategy();
}else if(Objects.equals(service, "noresult")){
// 没有返回值
return new NoResultStrategy();
}else if(Objects.equals(service, "blacklist")){
// 黑名单
return new BlackListStrategy();
}
return null;
}
}
总结
本文在策略模式上做了进一步的深入思考,我对比了一下简单工厂和策略工厂,这两个模式可以说长得还是非常像的,仅仅靠这些简单的案例是不够的,还需要更多的灵活运用。
标签:Java,策略,烟雨任,objectObjectHashMap,模式,工厂,new,设计模式,public 来源: https://blog.csdn.net/Jaeger_Java/article/details/113368244