要解決的問題
想象一下,對于某個任務箫措,我們需要支持多種解決方案腹备。而這多種支持就是變化點,為了封裝變換點斤蔓,我們可以采用策略模式植酥。
定義
定義了一系列的算法,把它們分別封裝起來,并且使它們可以相互替換友驮。
此模式讓算法的變化獨立于使用算法的客戶漂羊。
面向?qū)ο蟮姆绞?/h2>
UML
strategy-uml.png
如上圖所示,
-
Strategy
接口定義了算法的行為 - 一系列的算法
ConcreteStrategyA
和ConcreteStrategyB
分別實現(xiàn)了該接口 - 策略被包含在一個
Context
中卸留,擁有私有的strategy
實例走越,從而執(zhí)行具體的算法行為 - 通常
Context
中還會包含一個setStrategy
方法,從而動態(tài)改變策略
代碼
首先我們定義一個接口 IStrategy
interface IStrategy {
calculate(a: number, b: number): number
}
它要求實現(xiàn)一個名為 calculate
的方法耻瑟,該方法接受兩個 number
類型的參數(shù)旨指,最終返回一個 number
類型的值。
class AddStrategy implements IStrategy {
calculate(a: number, b: number): number {
return a + b
}
}
class SubtractStrategy implements IStrategy {
calculate(a: number, b: number): number {
return a - b
}
}
這里我們定義了兩個具體的策略類 AddStrategy
和 SubtractStrategy
喳整,它們都實現(xiàn)了 IStrategy
接口谆构。
最終我們來定義 Context
類:
class Context {
private strategy: IStrategy
constructor(strategy: IStrategy) {
this.strategy = strategy
}
public setStrategy(strategy: IStrategy) {
this.strategy = strategy
}
public calculate(a: number, b: number) {
return this.strategy.calculate(a, b)
}
}
類中包含一個私有的成員屬性 strategy
,可以通過構(gòu)造函數(shù)賦值框都,也可以通過 setStrategy
來動態(tài)改變搬素。最后 calculate
方法將會代理到具體的策略類上,并執(zhí)行具體的算法魏保。
const context = new Context(new AddStrategy())
console.log(context.calculate(1, 2)) // 3
context.setStrategy(new SubtractStrategy())
console.log(context.calculate(1, 2)) // -1
context.setStrategy(new SubtractStrategy())
console.log(context.calculate(1, 2)) // -1
帶上函數(shù)式的思考帽
我們再回過頭來思考策略的定義方式:
interface IStrategy {
calculate(a: number, b: number): number
}
這里的接口表達的意思是熬尺,期望一個類,擁有 calculate
方法囱淋,同時方法的簽名是 (number, number) -> number
猪杭。
其實真正想要的,只是里面的這個方法而已妥衣,我們大可不必將其放在類里面〗渖担可以把接口修改成如下的樣子:
interface IStrategy {
(a: number, b: number): number
}
這里 IStrategy
表達的意思是税手,一個函數(shù),類型為 (number, number) -> number
需纳。如有有些不好理解的話芦倒,我們可以將其改為 type
:
type IStrategy = (a: number, b: number) => number
那么相應的策略可以修改為:
const AddStrategy: IStrategy = (a, b) => a + b
const SubtractStrategy: IStrategy = (a, b) => a - b
這時,Context
就變成了:
class Context {
private strategy: IStrategy
constructor(strategy: IStrategy) {
this.strategy = strategy
}
public setStrategy(strategy: IStrategy) {
this.strategy = strategy
}
public calculate(a: number, b: number) {
return this.strategy(a, b)
}
}
const context = new Context(AddStrategy)
console.log(context.calculate(1, 2)) // 3
context.setStrategy(SubtractStrategy)
console.log(context.calculate(1, 2)) // -1
context.setStrategy(SubtractStrategy)
console.log(context.calculate(1, 2)) // -1
只是傳入一個函數(shù)而已不翩!去掉了類的層層包裹兵扬,一個函數(shù)就可以清晰明了地解決問題。
由于場景的設定口蝠,這里我們就不去將 Context
函數(shù)式化了器钟。
完整代碼
總結(jié)
策略模式,其目的是使不同算法族變得可控妙蔗。其方法是將符合統(tǒng)一接口的不同行為的類的實例注入進入傲霸。但是如果能使用函數(shù)式的方式,其實傳一個函數(shù)參數(shù),就可以了昙啄。
參考
- Head first design patterns
- https://cuipengfei.me/blog/2015/05/27/trait-and-fp-makes-strategy-pattern-irrelevant/