設計模式-委派/策略模式

1. 委派模式

1.1 委派模式的簡介

  • 委派模式不屬於 GOF23 種設計模式中。
  • 委派模式( Delegate Pattern )的基本做用就是負責任務的調用和分配任務,跟代理模式很像,能夠看作是一種特殊狀況下的靜態代理 的全權代理,可是代理模式注重過程,而委派模式注重結果。

1.2 委派模式的使用場景

  • 委派模式在 Spring 中應用很是多,你們經常使用的 DispatcherServlet 其實就是用到了委派模式。
  • 現實生活中也常有委 派的場景發生,例如:老闆(Boss)給項目經理(Leader)下達任務,項目經理會根據 實際狀況給每一個員工派發工做任務,待員工把工做任務完成以後,再由項目經理彙報工 做進度和結果給老闆。

1.3 場景實現

  • 上述工做中的場景是你們熟悉的,當 BossLeader 下發任務後, Leader 會根據實際狀況來分配給響應的組員,咱們將這一實際場景進行抽象化處理,用代碼來進行實現
  • 首先咱們要明確其中的關係,客戶請求(Boss)、委派者(Leader)、被委派者(Target) 在這個構建中 委派者與被委派者都服務與客戶請求,只是真實的操做時讓被委派者執行的,有點像靜態代理
  • 整體模型視圖以下:

  • 編寫 LeaderTarget 的共有父接口
public interface IEmployee {

    void doWork(String commd);
}
  • 編寫相應的實現類

編寫普通員工類:java

public class EmployeeA implements IEmployee {
    @Override
    public void doWork(String commd) {
        System.out.println("EmployeeA 正在處理 "+commd +"任務");
    }
}
public class EmployeeB implements IEmployee {
    @Override
    public void doWork(String commd) {
        System.out.println("EmployeeB 正在處理 "+commd +"任務");

    }
}

編寫 Leader 實現:算法

public class Leader implements IEmployee {

    private static Map<String,IEmployee> handlerMapping = new HashMap<>();
    public Leader(){
        //初始化規則
        handlerMapping.put("Login",new EmployeeA());
        handlerMapping.put("Pay",new EmployeeB());
    }
    @Override
    public void doWork(String commd) {
        handlerMapping.get(commd).doWork(commd);
    }
}
在初始化 Leader 時咱們首先將對應的規則記錄,也就是委派的規則,那些任務須要派給 A , 那麼任務須要派給 B ,後期的其餘需求也是在這裏進行擴展

編寫 Boss 類:設計模式

/**
 * @author: anonystar
 * @time: 2020/5/27 16:48
 */
public class Boss {

    private Leader leader;
    
    public Boss(Leader leader){
        this.leader = leader;
    }

    public void command(String cmd) {
        //委派分發
        leader.doWork(cmd);
    }
}

測試代碼:安全

/**
 * @author: anonystar
 * @time: 2020/5/28 9:40
 */
public class SimpleDelegateTest {

    public static void main(String[] args) {
        //客戶請求(Boss)、委派者(Leader)、被被委派者(Target)
        // 委派者要持有被委派者的引用
        // 代理模式注重的是過程, 委派模式注重的是結果
        // 策略模式注重是可擴展(外部擴展),委派模式注重內部的靈活和複用
        // 委派的核心:就是分發、調度、派遣
        // 
        Boss boss = new Boss(new Leader());
        boss.command("Pay");
    }
}

1.4 小結

  • 咱們經過上面代碼能夠發現委派模式就是靜態代理和策略模式一種特殊的組合
  • 代理模式注重的是過程, 委派模式更注重的是結果
  • 委派者要持有被委派者的引用
  • 委派的核心:就是分發、調度、派遣

2. 策略模式

2.1 策略模式簡介

  • 策略模式是一種行爲設計模式, 它能讓你定義一系列算法, 並將每種算法分別放入獨立的類中, 以使算法的對象可以相互替換。
  • 此模式讓算法的變化不會影響到使用算法的用戶

2.2 場景適用

  • 一、假如系統中有不少類,而他們的區別僅僅在於他們的行爲不一樣。
  • 二、一個系統須要動態地在幾種算法中選擇一種。

2.3 場景模擬

2.3.1 場景問題提出

前提:微信

  • 假設你爲旅遊者們設計了一款導遊程序。 該程序的核心功能是提供美觀的地圖, 以幫助用戶在任何城市中快速定位。
  • 用戶期待的程序新功能是自動路線規劃: 他們但願輸入地址後就能在地圖上看到前往目的地的最快路線。
  • 程序的首個版本只能規劃公路路線,這知足了駕車旅行的人們的需求,可是也很明顯的會忽略其餘選擇,因此你須要在一次次的迭代中增長新的規劃線路方案,如增長步行線路、公共交通線路等等。
  • 你覺得這樣就夠了?這只是個開始,沒多久時間你又要爲騎行者規劃路線。 又過了一段時間, 你又要爲遊覽城市中的全部景點規劃路線。此時相信面對不斷臃腫的代碼已經苦不堪言了,每次都的改動大量的代碼

實際問題:app

  • 每次線路的增長都讓整個開發團隊很是頭痛,由於每次增長新的線路規劃後整個代碼中的主體類都會增長一倍,慢慢的整個團都都沒法繼續維護這大量凌亂的代碼
  • 當在使用過程當中暴露出缺陷和某些功能的微調時,那麼對當前的修改都會影響到整個線路規劃,同時增長了程序運行中的其餘風險
  • 越到後期團隊合做將變得越低效。 尤爲在後期招募了新的團隊成員,他們須要大量的時間來熟悉和適應這些內容,同時在各類版本合併中掙扎。在實現新功能的過程當中, 你的團隊須要修改同一個巨大的類, 這樣他們所編寫的代碼相互之間就可能會出現衝突。

2.3.2 解決方案

  • 策略模式建議找出負責用許多不一樣方式完成特定任務的類, 而後將其中的算法抽取到一組被稱爲策略的獨立類中。

名爲上下文的原始類必須包含一個成員變量來存儲對於每種策略的引用。 上下文並不執行任務, 而是將工做委派給已鏈接的策略對象。ide

上下文不負責選擇符合任務須要的算法——客戶端會將所需策略傳遞給上下文。 實際上, 上下文並不十分了解策略, 它會經過一樣的通用接口與全部策略進行交互, 而該接口只需暴露一個方法來觸發所選策略中封裝的算法便可。函數

所以, 上下文可獨立於具體策略。 這樣你就可在不修改上下文代碼或其餘策略的狀況下添加新算法或修改已有算法了。測試

2.4 代碼實現

  • 構建路線頂級接口
/**
 * 路線接口
 * @author: anonystar
 * @url: i-code.online
 * @time: 2020/6/8 16:51
 */
public interface Route {

     String ROUTE_WALK = "walk";
     String ROUTE_CAR = "car";
     String ROUTE_CYCLING = "cycling";

    public void doRoute();
}
  • 實現具體線路方式 如步行線路、駕車線路、騎行線路等,均實現 Route 接口
/**
 * 駕車線路
 * @author: anonystar
 * @url: i-code.online
 * @time: 2020/6/8 16:58
 */
public class CarRoute implements Route {
    @Override
    public void doRoute() {
        System.out.println("======== 駕車線路 start =========");
    }
}
/**
 * 騎行線路
 * @author: anonystar
 * @url: i-code.online
 * @time: 2020/6/8 17:01
 */
public class CyclingRoute implements Route {
    @Override
    public void doRoute() {
        System.out.println("======== 騎行線路 start =========");
    }
}
/**
 * 步行線路
 * @author: anonystar
 * @url: i-code.online
 * @time: 2020/6/8 17:02
 */
public class WalkRoute implements Route {
    @Override
    public void doRoute() {
        System.out.println("======== 步行線路 start =========");

    }
}
  • 構建路線的上下文,做爲對外使用的惟一入口,調用全部的策略均從這裏使用
package org.strategy.travel;

/**
 * 構建路線 上下文
 * @author: anonystar
 * @url: i-code.online
 * @time: 2020/6/9 14:45
 */
public class RouteContext {

    // 上下文會維護指向某個策略對象的引用。上下文不知曉策略的具體類。
    // 上下文必須經過策略接口來與全部策略進行交互。
    private Route route;

    // 上下文一般會經過構造函數來接收策略對象,
    // 同時還提供設置器以便在運行時切換策略。
    public RouteContext(Route route){
        this.route = route;
    }
    public void setRoute(Route route) {
        this.route = route;
    }

    // 上下文會將一些工做委派給策略對象,而不是自行實現不一樣版本的算法。
    public void execute(){
        route.doRoute();
    }
}
  • 測試代碼
public void travle3(){
        String cmd = "walk";

        RouteContext route = null;
        if (cmd.equals(Route.ROUTE_WALK)){
            route = new RouteContext(new WalkRoute());
        }else if (cmd.equals(Route.ROUTE_CAR)){
            route = new RouteContext( new CarRoute());
        }
        route.execute();
    }
上面代碼咱們會發現若是有不少策略時,那麼會形成大量的if語句,這裏咱們可使用工廠模式來進行簡化,能夠看咱們以前的文章在i-code.online
  • 咱們構建一個工廠來簡化建立
package org.strategy.travel;

import java.util.HashMap;
import java.util.Map;

/**
 * 獲取上下文工廠
 * @author: anonystar
 * @url: i-code.online
 * @time: 2020/6/8 17:22
 */
public class RouteContextFactory {

    private static Map<String,Route> routeMap = new HashMap<>();

    private RouteContextFactory(){

    }

    static {
        routeMap.put(Route.ROUTE_CAR,new CarRoute());
        routeMap.put(Route.ROUTE_WALK,new WalkRoute());
        routeMap.put(Route.ROUTE_CYCLING,new CyclingRoute());
    }

    public static RouteContext getRoute(String cmd){
        Route route = routeMap.get(cmd);
        if ( null == route){
            route = routeMap.get(Route.ROUTE_CAR);
        }
        return new RouteContext(route);
    }
}
  • 測試代碼
/**
     * 經過工廠方法來簡化
     */
    public static void travle4(){
        String cmd = "car";
        RouteContext route = RouteContextFactory.getRoute(cmd);
        route.execute();
    }

2.5 使用場景

  • 當你想使用對象中各類不一樣的算法變體, 並但願能在運行時切換算法時, 可以使用策略模式。this

    策略模式讓你可以將對象關聯至能夠不一樣方式執行特定子任務的不一樣子對象, 從而以間接方式在運行時更改對象行爲。
  • 當你有許多僅在執行某些行爲時略有不一樣的類似類時, 可以使用策略模式。

    策略模式讓你能將不一樣行爲抽取到一個獨立類層次結構中, 並將原始類組合成同一個, 從而減小重複代碼。
  • 若是算法在上下文的邏輯中不是特別重要, 使用該模式能將類的業務邏輯與其算法實現細節隔離開來。

    策略模式讓你能將各類算法的代碼、 內部數據和依賴關係與其餘代碼隔離開來。 不一樣客戶端可經過一個簡單接口執行算法, 並能在運行時進行切換。
  • 當類中使用了複雜條件運算符以在同一算法的不一樣變體中切換時, 可以使用該模式。

    策略模式將全部繼承自一樣接口的算法抽取到獨立類中, 所以再也不須要條件語句。 原始對象並不實現全部算法的變體, 而是將執行工做委派給其中的一個獨立算法對象。

2.6 策略模式的優缺點

2.6.1 優勢:

  • 一、策略模式符合開閉原則。
  • 二、避免使用多重條件轉移語句,如 if...else... 語句、switch 語句
  • 三、使用策略模式能夠提升算法的保密性和安全性。

2.6.2 缺點:

  • 一、客戶端必須知道全部的策略,而且自行決定使用哪個策略類。
  • 二、代碼中會產生很是多策略類,增長維護難度
本文由AnonyStar 發佈,可轉載但需聲明原文出處。
仰慕「優雅編碼的藝術」 堅信熟能生巧,努力改變人生
歡迎關注微信公帳號 :雲棲簡碼 獲取更多優質文章
更多文章關注筆者博客 : 雲棲簡碼
相關文章
相關標籤/搜索