详解:设计模式之-策略设计模式

分享一波:程序员赚外快-必看的巅峰干货

什么是策略模式

定义一系列的算法,并将每一个算法单独进行封装,而且使它们可以相互替换,从而达到传递不同参数而执行不同算法的结果。

策略模式让算法独立于使用它的客户而独立变化

策略模式应用场景

策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。策略模式使得算法或逻辑可以在不影响到客户端的情况下发生变化。说到策略模式就不得不提及OCP(Open Closed Principle) 开闭原则,即对扩展开放,对修改关闭。策略模式的出现很好地诠释了开闭原则,有效地减少了分支语句。

基本案例

案例模拟购物车场景,根据不同的会员级别执行不同的折扣。分为初级、中级、高级会员三种。

//策略模式 定义抽象方法 所有支持公共接口
abstract class Strategy {

// 算法方法
abstract void algorithmInterface();

}

class StrategyA extends Strategy {

@Override
void algorithmInterface() {System.out.println("初级会员9.5折");}

}

class StrategyB extends Strategy {

@Override
void algorithmInterface() {System.out.println("中级会员8折");}

}

class StrategyC extends Strategy {

@Override
void algorithmInterface() {System.out.println("高级会员半价");}

}
// 使用上下文维护算法策略

class Context {

Strategy strategy;public Context(Strategy strategy) {this.strategy = strategy;
}public void algorithmInterface() {// 这里类似于代理模式,可以在方法的前后执行不同的逻辑strategy.algorithmInterface();
}

}

class ClientTestStrategy {
public static void main(String[] args) {
Context context;
context = new Context(new StrategyA());
context.algorithmInterface();
context = new Context(new StrategyB());
context.algorithmInterface();
context = new Context(new StrategyC());
context.algorithmInterface();

}

}

提高篇

在实际开发中,常常会使用到case语句,根据不同的情况执行不同的逻辑。当情况较多时,大量的case、if语句会极大地降低代码可读性,后期维护也极为不便,因此在实际开发中应当避免大量case语句的使用,此时,就可以使用策略模式进行代码重构。

在我的一个项目中,需要根据前端传递的不同值,而对sql进行不同的拼接,最先使用的是case语句,并对case中具体的逻辑抽取了方法,代码如下。

/**
* 获取查询条件
*
* @param params 参数
* @param asTable 表别名
* @param joinTables 连表
* @return
* @throws ParseException
*/
private String getWhere(Map<String, Object> params, String asTable,
Map<String, Map<String, Object>> joinTables) throws ParseException {
List expresses = Lists.newArrayList();
// 参数的key全部取出来封装成set集合
Set cloneSet = new HashSet<>(params.keySet());
for (String key : cloneSet) {
// 遍历key
// 值
String value;
// 存值集合
List values;
// 判断符号
switch (getOpt(key).toLowerCase()) {
case “in”:
// 如果是in
// 将其转换成 list
values = paramsToList(params, key);
if (values.isEmpty()) {
// 处理完之后如果为空就不处理
break;
} else {
in(params, asTable, expresses, key, values);
}
break;
case “notin”:
// 是notin,逻辑一样
// 将其转换成 list
values = paramsToList(params, key);
if (values.isEmpty()) {
break;
} else {
notIn(params, asTable, expresses, key, values);
}
break;
case “gt”:
// 是大于号
gt(params, asTable, expresses, key);
break;
case “lt”:
// 是小于号
lt(params, asTable, expresses, key);
break;
case “gte”:
// 是大于等于
gte(params, asTable, expresses, key);
break;
case “lte”:
// 是小于等于
lte(params, asTable, expresses, key);
break;
case “eq”:
// 是等于
eq(params, asTable, expresses, key);
break;
case “like”:
// 是like
like(params, asTable, expresses, key);
break;
case “btw”:
// 是between
value = params.get(key).toString();
// 默认只允许用这三种连接符
if (!value.contains("~") ||
!value.contains("-") ||
!value.contains("/")) {
break;
}
between(params, asTable, expresses, key, value);
break;
case “null”:
isNull(params, asTable, expresses, key);
break;
case “not”:
// 不等于
notEquals(params, asTable, expresses, key);
break;
case “match”:
match(params, asTable, expresses, key);
break;
case “join”:
//join_表名对应的实体类_操作_字段
//join_name_in_ids
joinTable(params, joinTables, key);
default:
// 处理所有的key(如果key没有前缀,就都默认为等于)
defaultCase(asTable, expresses, key);
break;
}
}
return StringUtils.join(expresses, " and ");
}

上面代码极不方便阅读,后面case可能继续增加,从而使代码的可维护性逐渐降低。

优化思路:对于不同的case情况,每个case即为一个枚举,而case中具体的方法则为一个策略。通过枚举的值来决定new哪个策略类,从而执行不同的代码逻辑。

思路很明确,就是把每个case作为一个策略,根据不同的case而执行不同的代码。

先编写条件策略的抽象类,具体的逻辑为抽象方法,供策略类去继承

public abstract class BaseOptStrategy {

/*** 处理option* @param params 参数* @param asTable 表别名* @param expresses 拆分后的数据* @param key #{param.key}*/
public abstract void sqlOptHandler(Map<String, Object> params, String asTable, List<String> expresses, String key);/*** 参数转列名。取出符号后的参数,转下划线** @param temp* @return*/
public static String getExpressColumn(String temp) {return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, temp.split("_")[1]);
}

}

再编写不同的策略,继承BaseOptStrategy,这里只用lte和eq两个case作为举例。

EqStrategy

@Getter
public class EqStrategy extends BaseOptStrategy {

public EqStrategy() {
}@Override
public void sqlOptHandler(Map<String, Object> params, String asTable, List<String> expresses, String key) {String value = params.get(key).toString();if (!StringUtils.isBlank(value)) {expresses.add(asTable + "." + getExpressColumn(key) + " = #{params." + key + "}");}
}

}

LteStrategy

@Getter
public class LteStrategy extends BaseOptStrategy {

public LteStrategy() {}@Override
public void sqlOptHandler(Map<String, Object> params, String asTable, List<String> expresses, String key) {String value = params.get(key).toString();if (!StringUtils.isBlank(value)) {expresses.add(asTable + "." + getExpressColumn(key) + " <= #{params." + key + "}");}
}

}

再编写执行策略的代理类

public class StrategyProxy {

private BaseOptStrategy strategy;public StrategyProxy(BaseOptStrategy strategy) {this.strategy = strategy;
}public void sqlOptHandler(Map<String, Object> params, String asTable, List<String> expresses, String key) {strategy.sqlOptHandler(params, asTable, expresses, key);
}

}

这样,对于每个eq和lte都有了对应的策略类,调用sqlOptHandler之前,传入不同的BaseOptStrategy就可以执行不同的逻辑。

但是,如何判断具体应该传入哪种策略?这里就不使用case语句,而是使用枚举,根据枚举值去new不同的对象

策略枚举代码

@Getter
public enum OptEnums {
/**
* 通过key去new对象
*/
IN(“in”, new InStrategy()),
NOTIN(“notin”, new NotInStrategy()),
GT(“gt”, new GtStrategy()),
LT(“lt”, new LtStrategy()),
GTE(“gte”, new GteStrategy()),
LTE(“lte”, new LteStrategy()),
EQ(“eq”, new EqStrategy()),
BTW(“btw”, new BtwStrategy()),
LIKE(“like”, new LikeStrategy()),
NULL(“null”, new NullStrategy()),
NOT(“not”, new NeqStrategy()),
MATCH(“match”, new MatchStrategy()),
JOIN(“join”, new JoinStrategy()),
;

private String opt;
private BaseOptStrategy strategy;OptEnums(String opt, BaseOptStrategy strategy) {this.opt = opt;this.strategy = strategy;
}

}

使用 OptEnums optEnums = Enum.valueOf(OptEnums.class, “枚举名称”); 就可以获取指定的枚举,优化后的getWhere代码如下

private String getWhere(Map<String, Object> params, String asTable,
Map<String, Map<String, Object>> joinTables) throws ParseException {
List expresses = Lists.newArrayList();
// 参数的key全部取出来封装成set集合
Set cloneSet = new HashSet<>(params.keySet());
for (String key : cloneSet) {
// 遍历key
Object value = params.get(key);
if (value != null && !"".equals(value)) {
String optName = getOpt(key).toUpperCase();
try {
OptEnums optEnums = Enum.valueOf(OptEnums.class, optName);
StrategyProxy factory = new StrategyProxy(optEnums.getStrategy());
factory.sqlOptHandler(params, asTable, expresses, key);
} catch (Exception e) {
String[] optAndExpressColumn = key.split("_");
if (optAndExpressColumn.length == 1) {
expresses.add(asTable + “.” + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, optAndExpressColumn[0])
+ “= #{params.” + key + “}”);
}
}
}
}
return StringUtils.join(expresses, " and ");
}

try中就是对不同的策略进行处理,代码简洁了很多。而default语句含义为,上面的case都不匹配的情况,而在枚举中,则对应为没有找到枚举的情况。在这里可以判断是否为空而决定执行case,也可以直接像上面代码一样try-catch。

事实上,上面代码依然可以优化,把catch中的代码作为默认执行策略,这里就不做过多的赘述。

策略模式是最符合OCP原则的设计模式,通过把不同的算法进行封装,程序去控制执行哪个代码,大大降低了代码的耦合性。

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537035.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql decode语句_MySQL复制问题的分析

s这是学习笔记的第 2031 篇文章最近有个业务的MySQL复制问题还是比较多&#xff0c;做了事务降维之后&#xff0c;把一些敏感操作和线上环境隔离起来&#xff0c;整体的效果好了许多&#xff0c;不过今天在外面的时候&#xff0c;又收到一条报警短信&#xff0c;让我心里咯噔一…

jvm形象简介之一看就懂

分享一波:程序员赚外快-必看的巅峰干货 据 JVM 规范&#xff0c;JVM 内存结构共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。这里的java内存结构与前面所述的Java内存模型是两个概念&#xff0c;不可以混淆。 堆 java堆是java虚拟机所管理的内存中最大的一块…

MZOJ 1345 hero

一道宽搜模版题,可写错了两个地方的我只得了56(掩面痛哭) http://10.37.2.111/problem.php?id1345 先看看正确的 #include <bits/stdc.h> #define read read() #define up(i,l,r) for(int i l; i<r; i) using namespace std;int read {int x 0; char ch getchar()…

opencv拖动进度条_OpenCV GUI基本操作,回调函数,进度条,裁剪图像等-阿里云开发者社区...

代码为转载&#xff0c;出处找不到了&#xff0c;不贴了工具条进度条&#xff1a;// ConvertColor.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include #include #include #pragma comment(lib,"opencv_core2410d.lib")#pragma comment(lib…

区间DP初探 P1880 [NOI1995]石子合并

https://www.luogu.org/problemnew/show/P1880 区间dp,顾名思义,是以区间为阶段的一种线性dp的拓展 状态常定义为$f[i][j]$,表示区间[i,j]的某种解; 通常先枚举区间长度,再枚举左端点,最后枚举断点(k) 石子合并便是一道经典的区间dp #include <bits/stdc.h> #define read…

jvm详解 - 新生代与老年代

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 Java 中的堆是 JVM 所管理的最大的一块内存空间&#xff0c;主要用于存放各种类的实例对象。 在 Java 中&#xff0c;堆被划分成两个不同的区…

pymysql建表_Python数据库操作,针对pymysql 和 MYSQL数据库

此文将以MYSQL数据库做为例子,pymysql库作为驱动进行学习安装MYSQL数据库与pymysql第三方库安装pymysql库不多做叙述安装navicat for mysql,此程序用来管理MYSQL数据库注意: 连接过程中可能会出现1251错误解决办法,在cmd命令下登录mysql后输入:ALTER USER rootlocalhost IDENTI…

从0到1使用VUE-CLI3开发实战(五):模块化VUEX及使用vuetify

小肆前几天发了一篇2019年Vue精品开源项目库的汇总&#xff0c;今天小肆要使用的是在UI组件中排行第三的Vuetify。vuetify介绍 Vuetify是一个渐进式的框架&#xff0c;完全根据Material Design规范开发&#xff0c;一共拥有80多个组件&#xff0c;对移动端支持非常好。 支持SSR…

详解垃圾回收算法

分享一波:程序员赚外快-必看的巅峰干货 标记清除算法 概念 该算法有两个阶段。 标记阶段&#xff1a;找到所有可访问的对象&#xff0c;做个标记。 清除阶段&#xff1a;遍历堆&#xff0c;把未被标记的对象回收 缺点&#xff1a;会产生碎片&#xff0c;不够连贯 应用场景…

智能情绪分析技术_石化缘推荐:炼化企业智能机器人巡检技术应用前景分析!...

本期内容由湖南天一奥星泵业有限公司冠名炼化企业智能机器人巡检技术应用前景分析王国彤1,孙秉才2,储胜利2,宋亚敏1(1.中国石油天然气股份有限公司大连石化分公司&#xff0c;辽宁省大连市&#xff1b;2.中国石油集团安全环保技术研究院有限公司&#xff0c;北京市)摘要&#x…

CentOS 7编译程序后的环境变量设置

今晚在 CentOS 7 上配置 Gitea&#xff0c;配置完成后在本地 clone 仓库会提示 Failed to execute git command: exec: "git-upload-pack": executable file not found in $PATH&#xff0c;果断用软连接打法解决。随后在 push 时又出现 Failed to execute git comma…

详解:JVM内存调优参数

分享一波:程序员赚外快-必看的巅峰干货 -Xms JVM启动时申请的初始Heap值&#xff0c;默认为操作系统物理内存的1/64但小于1G。默认当空余堆内存大于70%时&#xff0c;JVM会减小heap的大小到-Xms指定的大小&#xff0c;可通过-XX:MaxHeapFreeRation来指定这个比列。Server端JV…

数组指针 sizeof 实现_C++数组指针!

学习C数组的时候&#xff0c;对数组的了解不是很深。也不知道&#xff0c;为什么声明一个数组&#xff0c;int a[10]&#xff0c;为什么a就是数组的地址。你可以这样理解&#xff0c;将a理解为指向数组头的一个指针&#xff0c;这样就好理解了。理解了之后确实好像豁然开朗的样…

利用人工智能提升团队包容性

在2018年11月举行的Gartner应用技术与解决方案峰会上&#xff0c;高级主管分析师John Kostoulas认为&#xff0c;积极培养包容性文化的团队和团队领导者将超越他们的目标。Kostoulas引用了CEB-Gartner在2016年进行的一项领导力验证调查&#xff0c;他指出&#xff0c;性别多元化…

表单验证开发 - 登录注册开发(3)

表单验证开发 - 登录注册开发(3) 一、教程目标 学习如何在表单中添加验证规则。掌握使用 JSON 配置表单验证规则的方法。实现前端和后端的表单验证。 二、教程内容 1. 前端表单验证 步骤 1&#xff1a;找到表单编辑 在页面上找到需要编辑的表单&#xff0c;如注册表单或登录…

count(1),count(*),count(主键) 性能对比及辟谣

分享一波:程序员赚外快-必看的巅峰干货 前言 前段时间关于统计数量的sql问题和朋友进行了讨论&#xff0c;网上关于这三种查询方式说法不一&#xff0c;主要有以下两种说法。 count(*) count(主键) > count(1) count(主键) > count(*) > count(1)今天对这三种方式…

python与会计的论文_甭管前浪后浪,写完论文的先浪!

原标题&#xff1a;甭管前浪后浪&#xff0c;写完论文的先浪&#xff01;自愿返校已是板上钉钉的事儿了而对于大家的期末考现在也基本上已经通知线上考试如果没有线上考试的话&#xff0c;那就是交论文可是&#xff0c;论文动不动就2000字10%查重毛概、各种选修课等等每一门都是…

git 命令 clone分支的代码

一个项目通常含有很多分支&#xff0c; master分支一般是经过测试&#xff0c;验证没有问题后&#xff0c;代码才会提交到master分支 develop分支&#xff0c;是测试经常拉下来进行测试的分支 直接复制develop分支的git 命令如下&#xff1a; git clone -b develop gitxxx 转载…

String s = new String(123) 究竟创建了几个对象

分享一波:程序员赚外快-必看的巅峰干货 前言 今天上班划水的过程中有人询问到这个问题&#xff0c;网上对于这个问题也有争议&#xff0c;有说创建了一个对象&#xff0c;有说两个&#xff0c;有说三个。 首先说三个的肯定是扯淡了&#xff0c;今天来讨论一下这条语句到底创…

jquery级试题_JS-jQuery练习题面试题

ES5中不能实现继承的关键字A prototypeB callC applyD extends正确答案: D extends //属于ES6不属于常见23种设计模式A 单例B MVCC 观察者D 策略正确答案: B创建型模式&#xff0c;共五种&#xff1a;工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。结构型模式&…