要解決的問(wèn)題
想象一下,對(duì)于某個(gè)任務(wù),我們需要支持多種解決方案。而這多種支持就是變化點(diǎn),為了封裝變換點(diǎn),我們可以采用策略模式。
定義
定義了一系列的算法,把它們分別封裝起來(lái),并且使它們可以相互替換。
此模式讓算法的變化獨(dú)立于使用算法的客戶。
面向?qū)ο蟮姆绞?/h2>
UML
strategy-uml.png

如上圖所示,
-
Strategy接口定義了算法的行為 - 一系列的算法
ConcreteStrategyA和ConcreteStrategyB分別實(shí)現(xiàn)了該接口 - 策略被包含在一個(gè)
Context中,擁有私有的strategy實(shí)例,從而執(zhí)行具體的算法行為 - 通常
Context中還會(huì)包含一個(gè)setStrategy方法,從而動(dòng)態(tài)改變策略
代碼
首先我們定義一個(gè)接口 IStrategy
interface IStrategy {
calculate(a: number, b: number): number
}
它要求實(shí)現(xiàn)一個(gè)名為 calculate 的方法,該方法接受兩個(gè) number 類型的參數(shù),最終返回一個(gè) 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
}
}
這里我們定義了兩個(gè)具體的策略類 AddStrategy 和 SubtractStrategy,它們都實(shí)現(xiàn)了 IStrategy 接口。
最終我們來(lái)定義 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)
}
}
類中包含一個(gè)私有的成員屬性 strategy,可以通過(guò)構(gòu)造函數(shù)賦值,也可以通過(guò) setStrategy 來(lái)動(dòng)態(tài)改變。最后 calculate 方法將會(huì)代理到具體的策略類上,并執(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ù)式的思考帽
我們?cè)倩剡^(guò)頭來(lái)思考策略的定義方式:
interface IStrategy {
calculate(a: number, b: number): number
}
這里的接口表達(dá)的意思是,期望一個(gè)類,擁有 calculate 方法,同時(shí)方法的簽名是 (number, number) -> number。
其實(shí)真正想要的,只是里面的這個(gè)方法而已,我們大可不必將其放在類里面??梢园呀涌谛薷某扇缦碌臉幼樱?/p>
interface IStrategy {
(a: number, b: number): number
}
這里 IStrategy 表達(dá)的意思是,一個(gè)函數(shù),類型為 (number, number) -> number。如有有些不好理解的話,我們可以將其改為 type:
type IStrategy = (a: number, b: number) => number
那么相應(yīng)的策略可以修改為:
const AddStrategy: IStrategy = (a, b) => a + b
const SubtractStrategy: IStrategy = (a, b) => a - b
這時(shí),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
只是傳入一個(gè)函數(shù)而已!去掉了類的層層包裹,一個(gè)函數(shù)就可以清晰明了地解決問(wèn)題。
由于場(chǎng)景的設(shè)定,這里我們就不去將 Context 函數(shù)式化了。
完整代碼
總結(jié)
策略模式,其目的是使不同算法族變得可控。其方法是將符合統(tǒng)一接口的不同行為的類的實(shí)例注入進(jìn)入。但是如果能使用函數(shù)式的方式,其實(shí)傳一個(gè)函數(shù)參數(shù),就可以了。
參考
- Head first design patterns
- https://cuipengfei.me/blog/2015/05/27/trait-and-fp-makes-strategy-pattern-irrelevant/