Swift 中的 Strategy Pattern

前言

設計模式 中, 一個最基本的原則 少用繼承,多用合成 。所以我們有了 Strategy Pattern (策略模式) 和 其他有用的設計模式,在這邊主要是想要談如何透過 Swift 來實作策略模式。

關於 Strategy Pattern 策略模式

定義一系列演算法,將每一個演算法封裝起來,並讓它們可以相互替換。策略模式讓算法獨立於使用它的客戶而變化。

策略模式包含如下角色:

Context

Strategy

ConcreteStrategy

Imgur

實作Strategy Pettern

這邊使用 Swift 實作一個簡單的 Stategy Pattern 例子。

首先我們有一個 Sequenceclass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Sequence {
private var numbers:[Int]

init(_ numbers:Int...) {
self.numbers = numbers
}

func addNumber(value:Int) {
self.numbers.append(value)
}

func compute() -> Int {
return self.numbers.reduce(0, {$0 + $1})
}
}

那麼假設我今天想要在這類別增加新的功能呢? 通常是直接修改 Sequence 來增加或者修改原有的演算法。那麼我們來試著加入第二個演算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Sequence {
enum ALGORITHM {
case ADD; case MULTIPLY;
}
private var numbers:[Int]

init(_ numbers:Int...) {
self.numbers = numbers
}

func addNumber(value:Int) {
self.numbers.append(value)
}

func compute(algorithm:ALGORITHM) -> Int {
switch algorithm {
case .ADD:
return self.numbers.reduce(0, {$0 + $1})
case .MULTIPLY:
return self.numbers.reduce(1, {$0 * $1})
}
}
}

但是今天當我們需要增加多個演算法,或者是當大家分工在實作不同演算法時,我們就會出現一個問題,我們必須頻繁的修改 Sequence 類別,且每次修改都必須針對 func compute 方法,這樣的話就破壞了所謂的 封閉開放原則 - 類別應該開放擴充方式且對修改封閉 。那麼我們該如何做呢 ?

首先我們定義 stategycontext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protocol Stategy {
func execture(values:[Int]) -> Int;
}

class SumStrategy : Stategy {
func execture(values:[Int]) -> Int {
return values.reduce(0, {$0 + $1})
}
}

class MultiplyStrategy : Stategy {
func execture(values:[Int]) -> Int {
return values.reduce(1, {$0 * $1})
}
}

接著我們修改原本的 Sequence

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Sequence {

private var numbers:[Int]

init(_ numbers:Int...) {
self.numbers = numbers
}

func addNumber(value:Int) {
self.numbers.append(value)
}

func compute(algorithm:Stategy) -> Int {
return algorithm.execture(values: self.numbers)
}
}

之後我們要對 Sequence 增加新的演算法,例如相減,我們只需要實作 SubtractionStrategy 即可,並且像下列的方式使用 :

1
2
3
4
5
6
7
8
let sequence = Sequence(1, 2, 3, 4)
sequence.addNumber(value: 10)
sequence.addNumber(value: 20)

let sumStrategy = SumStrategy();
let multiplyStrategy = MultiplyStrategy();
sequence.compute(algorithm: sumStrategy)
sequence.compute(algorithm: multiplyStrategy)