詳解:設計模式之-策略設計模式

分享一波:程序員賺外快-必看的巔峰干貨

什么是策略模式

定義一系列的算法,并將每一個算法單獨進行封裝,而且使它們可以相互替換,從而達到傳遞不同參數而執行不同算法的結果。

策略模式讓算法獨立于使用它的客戶而獨立變化

策略模式應用場景

策略模式的用意是針對一組算法或邏輯,將每一個算法或邏輯封裝到具有共同接口的獨立的類中,從而使得它們之間可以相互替換。策略模式使得算法或邏輯可以在不影響到客戶端的情況下發生變化。說到策略模式就不得不提及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.pswp.cn/news/537035.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537035.shtml
英文地址,請注明出處:http://en.pswp.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;工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。結構型模式&…