策略模式原來就這麼簡單!
前言
只有光頭才能變強
無論是面試還是個人的提升,設計模式是必學的。今天來講解策略模式~
一、策略模式介紹
我一次聽到策略模式這個詞,是在我初學JDBC的時候。不知道大家有沒有用過DBUtils這個元件。當時初學跟著視訊學習,方立勳老師首先是讓我們先自己封裝一下JDBC的一些常用的操作(實際上就是模仿DBUtils這個元件)。
當時候的問題是這樣的:我們打算封裝一下 query()
查詢方法,傳入的引數有 Stringsql,Object[]objects
(指定SQL語句和對應的引數)。我們想根據不同的業務返回不同的值。
比如說,有的時候我們返回的是一條資料,那我們想將這條資料封裝成一個Bean物件
比如說,有的時候我們返回的是多條資料,那我們想將這多條資料封裝成一個
List<Bean>
集合比如說,有的時候我們返回的是xxxx資料,那我們想將這多條資料封裝成一個
Map<Bean>
集合........等等等
當時解決方案是這樣的:
先定義一個介面:ResultSetHandler(呼叫者想要對結果集進行什麼操作,只要實現這個介面即可)
這個介面定義了行為。
Objecthanlder(ResultSetresultSet);
然後實現上面的介面,比如我們要封裝成一個Bean物件,就是
publicclassBeanHandlerimplementsResultSetHandler
呼叫的時候,實際上就是
query()
查詢方法多一個引數query(Stringsql,Object[]objects,ResultSetHandlerrsh)
。呼叫者想要返回什麼型別,只要傳入相對應的ResultSetHandler實現類就是了。
程式碼如下:
query方法:
//這個方法的返回值是任意型別的,所以定義為Object。
public static Object query(String sql, Object[] objects, ResultSetHandler rsh) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = getConnection();
preparedStatement = connection.prepareStatement(sql);
//根據傳遞進來的引數,設定SQL佔位符的值
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
preparedStatement.setObject(i + 1, objects[i]);
}
}
resultSet = preparedStatement.executeQuery();
//呼叫呼叫者傳遞進來實現類的方法,對結果集進行操作
return rsh.hanlder(resultSet);
}
介面:
/*
* 定義對結果集操作的介面,呼叫者想要對結果集進行什麼操作,只要實現這個介面即可
* */
public interface ResultSetHandler {
Object hanlder(ResultSet resultSet);
}
介面實現類(Example):
//介面實現類,對結果集封裝成一個Bean物件
public class BeanHandler implements ResultSetHandler {
//要封裝成一個Bean物件,首先要知道Bean是什麼,這個也是呼叫者傳遞進來的。
private Class clazz;
public BeanHandler(Class clazz) {
this.clazz = clazz;
}
@Override
public Object hanlder(ResultSet resultSet) {
try {
//建立傳進物件的例項化
Object bean = clazz.newInstance();
if (resultSet.next()) {
//拿到結果集後設資料
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
//獲取到每列的列名
String columnName = resultSetMetaData.getColumnName(i+1);
//獲取到每列的資料
String columnData = resultSet.getString(i+1);
//設定Bean屬性
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(bean,columnData);
}
//返回Bean物件
return bean;
}
這就是策略模式??就這??這不是多型的使用嗎??
1.1策略模式講解
《設計模式之禪》:
定義一組演算法,將每個演算法都封裝起來,並且使他們之間可以互換
策略模式的類圖是這樣的:
策略的介面和具體的實現應該很好理解:
策略的介面相當於我們上面所講的ResultSetHandler介面(定義了策略的行為)
具體的實現相當於我們上面所講的BeanHandler實現(介面的具體實現)
具體的實現一般還會有幾個,比如可能還有ListBeanHandler、MapBeanHandler等等
令人想不明白的可能是:策略模式還有一個Context上下文物件。這物件是用來幹什麼的呢?
《設計模式之禪》:
Context叫做上下文角色,起承上啟下封裝作用,遮蔽高層模組對策略、演算法的直接訪問,封裝可能存在的變化。
在知乎上也有類似的問題(為什麼不直接呼叫,而要通過Person?):
說白了,通過Person來呼叫更符合物件導向(遮蔽了直接對具體實現的訪問)。
首先要明白一個道理,就是——到底是 “人” 旅遊,還是火車、汽車、自行車、飛機這些交通工具旅遊?
如果沒有上下文的話,客戶端就必須直接和具體的策略實現進行互動了,尤其是需要提供一些公共功能或者是儲存一些狀態的時候,會大大增加客戶端使用的難度;引入上下文之後,這部分工作可以由上下文來完成,客戶端只需要和上下文進行互動就可以了。這樣可以讓策略模式更具有整體性,客戶端也更加的簡單
具體的連結:
https://www.zhihu.com/question/31162942
所以我們再說回上文的通用類圖,我們就可以這樣看了:
1.2策略模式例子
現在3y擁有一個公眾號,名稱叫做Java3y。3y想要這讓更多的人認識到Java3y這個公眾號。所以每天都在想怎麼漲粉(hahah
於是3y就開始想辦法了(操碎了心),同時3y在這一段時間下來發現漲粉的方式有很多。為了方便,定義一個通用的介面方便來管理和使用唄。
介面:
/**
* 增加粉絲策略的介面(Strategy)
*/
interface IncreaseFansStrategy {
void action();
}
漲粉的具體措施,比如說,請水軍:
/**
* 請水軍(ConcreteStrategy)
*/
public class WaterArmy implements IncreaseFansStrategy {
@Override
public void action() {
System.out.println("3y牛逼,我要給你點贊、轉發、加雞腿!");
}
}
漲粉的具體措施,比如說,認真寫原創:
/**
* 認真寫原創(ConcreteStrategy)
*/
public class OriginalArticle implements IncreaseFansStrategy{
@Override
public void action() {
System.out.println("3y認真寫原創,最新一篇文章:《策略模式,就這?》");
}
}
3y還想到了很多漲粉的方法,比如說送書活動啊、商業互吹啊等等等...(這裡就不細說了)
說到底,無論是哪種漲粉方法,都是通過3y去執行的。
/**
* 3y(Context)
*/
public class Java3y {
private IncreaseFansStrategy strategy ;
public Java3y(IncreaseFansStrategy strategy) {
this.strategy = strategy;
}
// 3y要發文章了(買水軍了、送書了、寫知乎引流了...)。
// 具體執行哪個,看3y選哪個
public void exec() {
strategy.action();
}
}
所以啊,每當到了發推文的時候,3y就可以挑用哪種方式漲粉了:
public class Main {
public static void main(String[] args) {
// 今天2018年12月24日
Java3y java3y = new Java3y(new WaterArmy());
java3y.exec();
// 明天2018年12月25日
Java3y java4y = new Java3y(new OriginalArticle());
java4y.exec();
// ......
}
}
執行結果:
1.3策略模式優缺點
優點:
演算法可以自由切換
改一下策略很方便
擴充套件性良好
增加一個策略,就多增加一個類就好了。
缺點:
策略類的數量增多
每一個策略都是一個類,複用的可能性很小、類數量增多
所有的策略類都需要對外暴露
上層模組必須知道有哪些策略,然後才能決定使用哪一個策略
1.4JDK的策略模式應用
不知道大家還能不能想起ThreadPoolExecutor(執行緒池):執行緒池你真不來了解一下嗎?
學習ThreadPoolExecutor(執行緒池)就肯定要知道它的構造方法每個引數的意義:
/**
* Handler called when saturated or shutdown in execute.
*/
private volatile RejectedExecutionHandler handler;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//....
this.handler = handler;
}
/**
* Invokes the rejected execution handler for the given command.
* Package-protected for use by ScheduledThreadPoolExecutor.
*/
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
其中我們可以找到RejectedExecutionHandler,這個引數代表的是拒絕策略(有四種具體的實現:直接丟擲異常、使用呼叫者的執行緒來處理、直接丟掉這個任務、丟掉最老的任務)
其實這就是策略模式的體現了。
最後
看完會不會覺得策略模式特別簡單呀?就一個演算法介面、多個演算法實現、一個Context來包裝一下,就完事了。
推薦閱讀和參考資料:
https://www.cnblogs.com/lewis0077/p/5133812.html
《設計模式之禪》
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900354/viewspace-2286250/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 策略模式原來這麼簡單!模式
- 原來寫個Vue 首頁就這麼簡單Vue
- 常見 JavaScript 設計模式 — 原來這麼簡單JavaScript設計模式
- 瀑布流原來這麼簡單
- 掃一掃,原來這麼簡單
- Elasticsearch就這麼簡單Elasticsearch
- 原來 ArrayList 內部原理這麼簡單
- MySQL索引下推,原來這麼簡單!MySql索引
- 堆排序就這麼簡單排序
- 泛型就這麼簡單泛型
- 快速排序就這麼簡單排序
- 提高職場效率,原來可以這麼簡單
- SpringMVC入門就這麼簡單SpringMVC
- 基數排序就這麼簡單排序
- 歸併排序就這麼簡單排序
- 插入排序就這麼簡單排序
- 選擇排序就這麼簡單排序
- SpringDataJPA入門就這麼簡單Spring
- 氣泡排序就這麼簡單排序
- 哈?原來css網格佈局這麼簡單!!!CSS
- 用idea搭建SSM專案,原來這麼簡單IdeaSSM
- LinkedHashMap就這麼簡單【原始碼剖析】HashMap原始碼
- TreeMap就這麼簡單【原始碼剖析】原始碼
- 策略模式簡單運用模式
- 建造者模式就是這麼簡單模式
- List集合就這麼簡單【原始碼剖析】原始碼
- SOLIDWORKS編碼重新命名批次完成原來這麼簡單Solid
- 包裝模式就是這麼簡單啦模式
- 用 Java 訓練深度學習模型,原來可以這麼簡單!Java深度學習模型
- SpringBoot獲取配置檔案,就這麼簡單。Spring Boot
- zookeeper核心之ZAB協議就這麼簡單!協議
- Java實現一個棧就這麼簡單Java
- kotlin代理模式就是這麼簡單(委託)Kotlin模式
- Druid資料庫連線池就這麼簡單UI資料庫
- 百萬點贊怎麼來?Python批量製作抖音的卡點視訊原來這麼簡單!Python
- 簡單幾步讓你的Excel表格變漂亮,學會Excel就這麼簡單!Excel
- Java 關於策略模式+簡單工廠模式下的思考Java模式
- Linux併發執行很簡單,這麼幹就對了Linux