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

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

什么是策略模式

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

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

策略模式应用场景

策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。策略模式使得算法或逻辑可以在不影响到客户端的情况下发生变化。说到策略模式就不得不提及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虚拟机所管理的内存中最大的一块…

jvm详解 - 新生代与老年代

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

从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…

详解: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;这样就好理解了。理解了之后确实好像豁然开朗的样…

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

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

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

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

Vue 计算属性与侦听器

这一节我们一起学习 vue 中的计算属性(computed properties)和侦听器(watch)。 在之前&#xff0c;我们学习过 vue 表达式插值&#xff1a; <div id"example">{{ message.split().reverse().join() }} </div> 如果在模板中放入太多的逻辑会让模板过重且难…

程序员到底要不要重复造轮子?

分享一波:程序员赚外快-必看的巅峰干货 关于这个话题&#xff0c;现在这里阐述立场&#xff1a;就公司工作而言&#xff0c;不建议重复造轮子。就个人技术而言&#xff0c;强烈建议造轮子&#xff01; 程序员圈子里流行这么一句话&#xff1a;“不要重复造轮子”。它的原文是…

1582年日历怎么了_【知乎周边】知乎2020年日历开箱+测评

感谢 刘看山 刘看山福利社 知一声 这边知乎朋友赠送的礼物&#xff0c;这边拿到了新的一年2020年知乎的日历。随日历还赠送了一年的盐选会员体验卡&#xff0c;这个福利很特别哈。打开盒子&#xff0c;里面是厚厚的但是却不是很大的一个正方体。侧面写有“有问题的日历”日历内…

JDK8那些惊为天人的新特性

分享一波:程序员赚外快-必看的巅峰干货 介绍 随着java的发展&#xff0c;越来越多的企业开始使用 java8 版本。Java8 是自 java5之后最重要的版本&#xff0c;这个版本包含语言、编译器、库、工具、JVM等方面的十多个新特性。本次课程将着重学习其中的一些重点特性。 Jdk8新…

【数据结构和算法05】 红-黑树(转发)

2019独角兽企业重金招聘Python工程师标准>>> 【数据结构和算法05】 红-黑树&#xff08;看完包懂~&#xff09; 置顶 2016年04月13日 15:50:25 eson_15 阅读数&#xff1a;52681 标签&#xff1a; java数据结构算法红黑树 更多 个人分类&#xff1a; ● 结构算法---…

数据结构与算法——二叉树、堆、优先队列

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 七、树 7.1 树 7.1.1 树的定义 树是我们计算机中非常重要的一种数据结构&#xff0c;同时使用树这种数据结构&#xff0c;可以描述现实生活…

继牛津大学后,加大伯克利分校等多家美国高校终止与华为合作

文&#xff0f;AI财经社 唐煜编&#xff0f;嵇国华据 Nature News 报道&#xff0c;在美国相关部门的压力之下&#xff0c;加州大学伯克利分校&#xff08;UC Berkeley&#xff09;近日宣布不再与华为签署新的研究合作&#xff1b;德州大学奥斯丁分校也正在审查自身与华为的关系…

为什么varchar字段长度最好是2的n次方-1

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 计算机是二进制计算的&#xff0c;1 bytes 8 bit ,一个字节最多可以代表的数据长度是2的8次方 11111111 在计算机中也就是-128到127。 而var…

Python之celery的简介与使用

celery的简介 celery是一个基于分布式消息传输的异步任务队列&#xff0c;它专注于实时处理&#xff0c;同时也支持任务调度。它的执行单元为任务&#xff08;task&#xff09;&#xff0c;利用多线程&#xff0c;如Eventlet&#xff0c;gevent等&#xff0c;它们能被并发地执行…

不使用比较运算符如何比较两个数的大小

分享一波:程序员赚外快-必看的巅峰干货 前言 今天在水群的过程中看到有位群员谈论到这个话题&#xff0c;是他找工作过程中某家公司的面试题&#xff08;到底是哪家公司才会出这种没营养的题目刁难别人&#xff09;&#xff0c;有点兴趣&#xff0c;就开始写了。 开搞 想了一…