天天看點

GOF設計模式-對象行為型模式-政策模式

算法的封裝與切換——政策模式

寫代碼時總會出很多的if…else,或者case。如果在一個條件語句中又包含了多個條件語句就會使得代碼變得臃腫,維護的成本也會加大,而政策模式就能較好的解決這個問題,本篇部落格就帶你詳細了解政策模式。

政策模式概述

在政策模式中,我們可以定義一些獨立的類來封裝不同的算法,每一個類封裝一種具體的算 法,在這裡,每一個封裝算法的類我們都可以稱之為一種政策(Strategy),為了保證這些政策 在使用時具有一緻性,一般會提供一個抽象的政策類來做規則的定義,而每種算法則對應于 一個具體政策類。

政策模式的主要目的是将算法的定義與使用分開,也就是将算法的行為和環境分開,将算法 的定義放在專門的政策類中,每一個政策類封裝了一種實作算法,使用算法的環境類針對抽 象政策類進行程式設計,符合“依賴倒轉原則”。在出現新的算法時,隻需要增加一個新的實作了抽 象政策類的具體政策類即可。政策模式定義如下: 政策模式(Strategy Pattern):定義一系列算 法類,将每一個算法封裝起來,并讓它們可以互相替換,政策模式讓算法獨立于使用它的客 戶而變化,也稱為政策模式(Policy)。政策模式是一種對象行為型模式。

政策模式結構并不複雜,但我們需要了解其中環境類Context的作用,其結構如圖所示:

GOF設計模式-對象行為型模式-政策模式

在政策模式結構圖中包含如下幾個角色:

● Context(環境類):環境類是使用算法的角色,它在解決某個問題(即實作某個方法)時 可以采用多種政策。在環境類中維持一個對抽象政策類的引用執行個體,用于定義所采用的策 略。

● Strategy(抽象政策類):它為所支援的算法聲明了抽象方法,是所有政策類的父類,它可 以是抽象類或具體類,也可以是接口。環境類通過抽象政策類中聲明的方法在運作時調用具 體政策類中實作的算法。

● ConcreteStrategy(具體政策類):它實作了在抽象政策類中聲明的算法,在運作時,具體策 略類将覆寫在環境類中定義的抽象政策類對象,使用一種具體的算法實作某個業務處理。

政策模式是一個比較容易了解和使用的設計模式,政策模式是對算法的封裝,它把算法的責 任和算法本身分割開,委派給不同的對象管理。政策模式通常把一個系列的算法封裝到一系 列具體政策類裡面,作為抽象政策類的子類。在政策模式中,對環境類和抽象政策類的了解 非常重要,環境類是需要使用算法的類。在一個系統中可以存在多個環境類,它們可能需要 重用一些相同的算法。

政策模式的典型代碼如下:

抽象政策類

public interface Strategy {
    /**
     * 政策方法
     */
    public void strategyInterface();
}
      

具體政策類

public class ConcreteStrategyA implements Strategy {

    @Override
    public void strategyInterface() {
        //相關的業務
    }

}
      
public class ConcreteStrategyB implements Strategy {

    @Override
    public void strategyInterface() {
        //相關的業務
    }

}
      

環境角色類

public class Context {
    //持有一個具體政策的對象
    private Strategy strategy;
    /**
     * 構造函數,傳入一個具體政策對象
     * @param strategy    具體政策對象
     */
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    /**
     * 政策方法
     */
    public void contextInterface(){

        strategy.strategyInterface();
    }

}
      

政策模式使用場景舉例:原文:https://blog.csdn.net/u012124438/article/details/70039943 

假設鵝廠推出了3種會員,分别為會員,超級會員以及金牌會員,還有就是普通玩家,針對不同類别的玩家,購買《王者農藥》皮膚有不同的打折方式,并且一個顧客每消費10000就增加一個級别,那麼我們就可以使用政策模式,因為政策模式描述的就是算法的不同,這裡我們舉例就采用最簡單的,以上四種玩家分别采用原價(普通玩家),九折,八折和七價的收錢方式。

那麼我們首先要有一個計算價格的政策接口

public interface CalPrice {
    //根據原價傳回一個最終的價格
    Double calPrice(Double orgnicPrice);
}      

下面是4種玩家的計算方式的實作

//普通玩家類:具體政策類

public class Orgnic implements CalPrice {

    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice;
    }
}
      

//VIP類:具體政策類

public class Vip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.9;
    }
}           

//超級會員類:具體政策類

public class SuperVip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.8;
    }
}
      

//金牌會員類:具體政策類

public class GoldVip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.7;
    }
}
      

我們看客戶類,我們需要客戶類幫我們完成玩家更新的功能。

public class Player {
    private Double totalAmount = 0D;//客戶在鵝廠消費的總額
    private Double amount = 0D;//客戶單次消費金額
    private CalPrice calPrice = new Orgnic();//每個客戶都有一個計算價格的政策,初始都是普通計算,即原價

    //客戶購買皮膚,就會增加它的總額
    public void buy(Double amount) {
        this.amount = amount;
        totalAmount += amount;
        if (totalAmount > 30000) {//30000則改為金牌會員計算方式
            calPrice = new GoldVip();
        } else if (totalAmount > 20000) {//類似
            calPrice = new SuperVip();
        } else if (totalAmount > 10000) {//類似
            calPrice = new Vip();
        }
    }

    //計算客戶最終要付的錢
    public Double calLastAmount() {
        return calPrice.calPrice(amount);
    }
}
      

接下來是用戶端調用,系統會幫我們自動調整收費政策。

public class Client {
    public static void main(String[] args) {
        Player player = new Player();
        player.buy(5000D);
        System.out.println("玩家需要付錢:" + player.calLastAmount());
        player.buy(12000D);
        System.out.println("玩家需要付錢:" + player.calLastAmount());
        player.buy(12000D);
        System.out.println("玩家需要付錢:" + player.calLastAmount());
        player.buy(12000D);
        System.out.println("玩家需要付錢:" + player.calLastAmount());
    }
}
      

運作以後會發現,第一次是原價,第二次是九折,第三次是八折,最後一次則是七價。這樣設計的好處是,客戶不再依賴于具體的收費政策,依賴于抽象永遠是正确的。

政策模式總結

政策模式用于算法的自由切換和擴充,它是應用較為廣泛的設計模式之一。政策模式對應于 解決某一問題的一個算法族,允許使用者從該算法族中任選一個算法來解決某一問題,同時可 以友善地更換算法或者增加新的算法。隻要涉及到算法的封裝、複用和切換都可以考慮使用 政策模式。

1. 主要優點

政策模式的主要優點如下:

(1) 政策模式提供了對“開閉原則”的完美支援,使用者可以在不修改原有系統的基礎上選擇算法 或行為,也可以靈活地增加新的算法或行為。

(2) 政策模式提供了管理相關的算法族的辦法。政策類的等級結構定義了一個算法或行為族, 恰當使用繼承可以把公共的代碼移到抽象政策類中,進而避免重複的代碼。

(3) 政策模式提供了一種可以替換繼承關系的辦法。如果不使用政策模式,那麼使用算法的環 境類就可能會有一些子類,每一個子類提供一種不同的算法。但是,這樣一來算法的使用就 和算法本身混在一起,不符合“單一職責原則”,決定使用哪一種算法的邏輯和該算法本身混合 在一起,進而不可能再獨立演化;而且使用繼承無法實作算法或行為在程式運作時的動态切 換。

(4) 使用政策模式可以避免多重條件選擇語句。多重條件選擇語句不易維護,它把采取哪一種 算法或行為的邏輯與算法或行為本身的實作邏輯混合在一起,将它們全部寫死(Hard Coding) 在一個龐大的多重條件選擇語句中,比直接繼承環境類的辦法還要原始和落後。

(5) 政策模式提供了一種算法的複用機制,由于将算法單獨提取出來封裝在政策類中,是以不 同的環境類可以友善地複用這些政策類。

1. 主要缺點

政策模式的主要缺點如下:

(1) 用戶端必須知道所有的政策類,并自行決定使用哪一個政策類。這就意味着用戶端必須理 解這些算法的差別,以便适時選擇恰當的算法。換言之,政策模式隻适用于用戶端知道所有 的算法或行為的情況。

(2) 政策模式将造成系統産生很多具體政策類,任何細小的變化都将導緻系統要增加一個新的 具體政策類。

(3) 無法同時在用戶端使用多個政策類,也就是說,在使用政策模式時,用戶端每次隻能使用 一個政策類,不支援使用一個政策類完成部分功能後再使用另一個政策類來完成剩餘功能的 情況。

1. 适用場景

在以下情況下可以考慮使用政策模式:

(1) 一個系統需要動态地在幾種算法中選擇一種,那麼可以将這些算法封裝到一個個的具體算 法類中,而這些具體算法類都是一個抽象算法類的子類。換言之,這些具體算法類均有統一 的接口,根據“裡氏代換原則”和面向對象的多态性,用戶端可以選擇使用任何一個具體算法 類,并隻需要維持一個資料類型是抽象算法類的對象。

(2) 一個對象有很多的行為,如果不用恰當的模式,這些行為就隻好使用多重條件選擇語句來 實作。此時,使用政策模式,把這些行為轉移到相應的具體政策類裡面,就可以避免使用難 以維護的多重條件選擇語句。

(3) 不希望用戶端知道複雜的、與算法相關的資料結構,在具體政策類中封裝算法與相關的數 據結構,可以提高算法的保密性與安全性。