政策模式(Strategy)屬于對象的行為模式。其用意是針對一組算法,将每一個算法封裝到具有共同接口的獨立的類中,進而使得它們可以互相替換。政策模式使得算法可以在不影響到用戶端的情況下發生變化。 (學習)
政策模式的結構
政策模式是對算法的包裝,是把使用算法的責任和算法本身分割開來,委派給不同的對象管理。政策模式通常把一個系列的算法包裝到一系列的政策類裡面,作為一個抽象政策的子類。用一句話來說,就是:“準備一組算法,并将每一個算法封裝起來,使得它們可以互換”。下面就以一個示意性的實作講解政策模式執行個體的結構。
這個模式涉及到三個角色:
- 環境(Context)角色:持有一個Strategy的引用。
- 抽象政策(Strategy)角色:這是一個抽象角色,通常由一個接口或抽象類實作。此角色給出所有的具體政策類所需的接口。
- 具體政策(ConcreteStrategy)角色:包裝了相關的算法或行為。
源代碼
package Strategy
/**
* 環境角色類
* */
class Context constructor(strategy: Strategy) {
/**
* 持有一個具體政策的對象
* */
private var strategy: Strategy? = null
init {
this.strategy = strategy
}
/**
* 政策方法
* */
fun contextInteface() {
strategy?.strategyInterface()
}
}
package Strategy
/**
* 抽象政策類
* */
interface Strategy {
/**
* 政策方法
* */
fun strategyInterface()
}
package Strategy
/**
* 具體政策類
* */
class ConcreteStrategyA : Strategy {
override fun strategyInterface() {
println("ConcreteStrategyA")
}
}
package Strategy
class ConcreteStrategyB : Strategy{
override fun strategyInterface() {
println("ConcreteStrategyB")
}
}
使用場景
假設現在要設計一個販賣各類書籍的電子商務網站的購物車系統。一個最簡單的情況就是把所有貨品的單價乘上數量,但是實際情況肯定比這要複雜。比如,本網站可能對所有的進階會員提供每本20%的促銷折扣;對中級會員提供每本10%的促銷折扣;對初級會員沒有折扣。
根據描述,折扣是根據以下的幾個算法中的一個進行的:
算法一:對初級會員沒有折扣;
算法二:對中級會員提供10%的促銷折扣;
算法三:對進階會員提供20%的促銷折扣;
使用政策模式來實作的結構圖如下:
package Strategy.example
/**
* 抽象折扣類
* */
interface MemberStrategy {
/**
* 計算圖書的價格
* @param 圖書原價
* @return 計算出打折後的價格
* */
fun calcPrice(bookPrice: Double): Double
}
package Strategy.example
/**
* 初級會員折扣類
* */
class PrimaryMemberStrategy : MemberStrategy {
override fun calcPrice(bookPrice: Double): Double {
println("對于初級會員的沒有節扣")
return bookPrice
}
}
package Strategy.example
/**
* 中級會員折扣類
* */
class IntermediateMemberStrategy : MemberStrategy {
override fun calcPrice(bookPrice: Double): Double {
println("中級會員的折扣為:10%")
return bookPrice * 0.9
}
}
package Strategy.example
/**
* 進階會員折扣類
* */
class AdvancedMemberStrategy : MemberStrategy {
override fun calcPrice(bookPrice: Double): Double {
println("進階會員的折扣為:20%")
return bookPrice * 0.8
}
}
package Strategy.example
/**
* 價格類
* */
class Price constructor(memberStrategy: MemberStrategy) {
/**
* 持有一個具體的政策對象
* */
private var strategy: MemberStrategy? = null
init {
this.strategy = memberStrategy
}
/**
* 計算出圖書的價格
* @param bookPrice 圖書的原價格
* @return 計算出打折後的價格
* */
fun quote(bookPrice: Double): Double? {
return this.strategy?.calcPrice(bookPrice)
}
}
用戶端運作
//選擇并建立需要使用的政策對象
val memberStrategy = AdvancedMemberStrategy()
//建立環境
val price = Price(memberStrategy)
//計算價格
val quote = price.quote(300.0)
println("圖書價格為:$quote")
運作結果
進階會員的折扣為:20%
圖書價格為:240.0
從上面的示例可以看出,政策模式僅僅封裝算法,提供新的算法插入到已有系統中,以及老算法從系統中“退休”的方法,政策模式并不決定在何時使用何種算法。在什麼情況下使用什麼算法是由用戶端決定的。
認識政策模式
政策模式的重心
政策模式的重心不是如何實作算法,而是如何組織、調用這些算法,進而讓程式結構更靈活,具有更好的維護性和擴充性。
算法的平等性
政策模式一個很大的特點就是各個政策算法的平等性。對于一系列具體的政策算法,大家的地位是完全一樣的,正因為這個平等性,才能實作算法之間可以互相替換。所有的政策算法在實作上也是互相獨立的,互相之間是沒有依賴的。
是以可以這樣描述這一系列政策算法:政策算法是相同行為的不同實作。
運作時政策的唯一性
運作期間,政策模式在每一個時刻隻能使用一個具體的政策實作對象,雖然可以動态地在不同的政策實作中切換,但是同時隻能使用一個。
公有的行為
經常見到的是,所有的具體政策類都有一些公有行為。這時候,就應當把這些公有的行為放到共同的抽象政策角色Stretegy類裡面。當然這時候抽象政策角色必順要用Java抽象類實作,而不能使用接口。
這其實也是典型的将代碼向繼承等級結構的上方集中的标準做法。
政策模式的優點
- 政策模式提供了管理相關的算法族的方法。政策類的等級結構定義了一個算法或行為旅。恰當使用繼承可以把公共的代碼移到父類裡面,進而避免代碼重複。
- 使用政策模式可以避免使用多重條件(if-else)語句。多重條件語句不易維護,它把采取哪一種算法或采取哪一種行為的邏輯與算法或行為的邏輯混合在一起,統統列在一個多重條件語句裡面,比使用繼承的辦法還要原始與落後。
政策模式的缺點
- 用戶端必順知道所有的政策類,并自決定使用哪一個政策類。這不是意味着用戶端必順了解這些算法的差別,以便适時選擇恰當的算法類。換言之,政策模式隻适用于用戶端知道算法或行為的情況。
- 由于政策模式把生個具體的政策實作都單獨封裝成為類,如果備選的政策很多的話,那麼對象的數目就會很多。