天天看點

政策模式原來就這麼簡單!

前言

隻有光頭才能變強

回顧前面:

  • 給女朋友講解什麼是代理模式
  • 包裝模式就是這麼簡單啦
  • 單例模式你會幾種寫法?
  • 工廠模式了解了沒有?

無論是面試還是個人的提升,設計模式是必學的。今天來講解政策模式~

一、政策模式介紹

我一次聽到政策模式這個詞,是在我初學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來調用更符合面向對象(屏蔽了直接對具體實作的通路)。

首先要明白一個道理,就是——到底是 “人” 旅遊,還是火車、汽車、自行車、飛機這些交通工具旅遊?

如果沒有上下文的話,用戶端就必須直接和具體的政策實作進行互動了,尤其是需要提供一些公共功能或者是存儲一些狀态的時候,會大大增加用戶端使用的難度;引入上下文之後,這部分工作可以由上下文來完成,用戶端隻需要和上下文進行互動就可以了。這樣可以讓政策模式更具有整體性,用戶端也更加的簡單

是以我們再說回上文的通用類圖,我們就可以這樣看了:

政策模式原來就這麼簡單!

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來包裝一下,就完事了。

推薦閱讀和參考資料:

  • 《設計模式之禅》
政策模式原來就這麼簡單!

繼續閱讀