只有光頭才能變強
回顧前面:html
不管是面試仍是我的的提高,設計模式是必學的。今天來說解策略模式~java
我一次聽到策略模式這個詞,是在我初學JDBC的時候。不知道你們有沒有用過DBUtils這個組件。當時初學跟着視頻學習,方立勳老師首先是讓咱們先本身封裝一下JDBC的一些經常使用的操做(實際上就是模仿DBUtils這個組件)。git
當時候的問題是這樣的:咱們打算封裝一下query()
查詢方法,傳入的參數有String sql , Object[] objects
(指定SQL語句和對應的參數)。咱們想根據不一樣的業務返回不一樣的值。github
List<Bean>
集合Map<Bean>
集合當時解決方案是這樣的:面試
先定義一個接口:ResultSetHandler(調用者想要對結果集進行什麼操做,只要實現這個接口便可)算法
Object hanlder(ResultSet resultSet);
public class BeanHandler implements ResultSetHandler
query()
查詢方法多一個參數 query(String sql, Object[] objects, ResultSetHandler rsh)
。調用者想要返回什麼類型,只要傳入相對應的ResultSetHandler實現類就是了。代碼以下:sql
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; }
這就是策略模式??就這??這不是多態的使用嗎??設計模式
《設計模式之禪》:ide
定義一組算法,將每一個算法都封裝起來,而且使他們之間能夠互換
策略模式的類圖是這樣的:工具
策略的接口和具體的實現應該很好理解:
具體的實現至關於咱們上面所講的BeanHandler實現(接口的具體實現)
使人想不明白的多是:策略模式還有一個Context上下文對象。這對象是用來幹什麼的呢?
《設計模式之禪》:
Context叫作上下文角色,起承上啓下封裝做用, 屏蔽高層模塊對策略、算法的直接訪問,封裝可能存在的變化。
在知乎上也有相似的問題(爲何不直接調用,而要經過Person?):
說白了,經過Person來調用更符合面向對象(屏蔽了直接對具體實現的訪問)。
首先要明白一個道理,就是——究竟是 「人」 旅遊,仍是火車、汽車、自行車、飛機這些交通工具旅遊?
若是沒有上下文的話,客戶端就必須直接和具體的策略實現進行交互了,尤爲是須要提供一些公共功能或者是存儲一些狀態的時候,會大大增長客戶端使用的難度;引入上下文以後,這部分工做能夠由上下文來完成,客戶端只須要和上下文進行交互就能夠了。這樣可讓策略模式更具備總體性,客戶端也更加的簡單
具體的連接:
因此咱們再說回上文的通用類圖,咱們就能夠這樣看了:
如今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(); // ...... } }
執行結果:
優勢:
算法能夠自由切換
擴展性良好
缺點:
策略類的數量增多
全部的策略類都須要對外暴露
不知道你們還能不能想起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來包裝一下,就完事了。
推薦閱讀和參考資料:
樂於分享和輸出 乾貨的Java技術公衆號:Java3y。
文章的目錄導航: