設計模式(design pattern)主要分爲如下三種類型:git
結構型模式(structural design pattern):描述對象的構成,以及如何組合對象以造成更大的結構,也就是了解元件間關係,以簡化設計。Structural design patter 包含 Model-View-Controller (MVC)、Model-View-ViewModel (MVVM) 和外觀模式 (Facade)。github
行爲型模式(behavioral design pattern):描述對象間如何通訊,以便在進行這些交流活動時加強彈性。Behavioral design pattern 包含 Delegation、Strategy、Observer。編程
建立型模式(creational design pattern):處理對象如何建立,根據實際狀況使用合適的方式建立對象。基本的對象建立方式可能會致使設計上的問題,或增長設計的複雜度,creational design pattern 經過以某種方式控制對象的建立來解決問題。Creational design pattern 包含 Builder、Singleton 和 Prototype。swift
策略模式 strategy pattern 屬於 behavioral pattern。Strategy pattern 定義了一系列可互換替代的對象,能夠在 runtime 時設置或切換。策略模式包含如下三部分:設計模式
使用策略模式的對象:一般爲視圖控制器,也能夠是任何有互換替代 (interchangeable) 需求的對象。bash
Strategy protocol:每種策略都必須遵照的協議。app
Strategies:遵照 strategy protocol 協議的對象,相互間可互換代替。ide
當有兩種或更多可相互代替的行爲時,使用策略模式。函數
Strategy pattern 和 Delegation pattern 很像。均依賴 protocol 而非具體對象以增長靈活性。所以,任何實現了 strategy protocol 的對象在運行時都可做爲一種策略。與委託不一樣的是,strategy pattern 使用一族對象。單元測試
Delegate 在運行時固定不變。例如,UITableView的dataSource和delegate設置後不須要改變。Strategy 在運行時能夠相互切換。
Demo 是購物車結算模塊。結算方式有正常價格、九折和免運費三種。
更新ShoppingCartViewController.swift文件中prepare(for:sender:)方法:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
view.endEditing(false)
if let destinationViewController: DetailViewController = segue.destination as? DetailViewController {
destinationViewController.bindProperties(itemPrices: itemPrices, checkoutType: .normal)
}
}
複製代碼
在TotalPriceViewController.swift文件添加如下方法:
public func bindProperties(itemPrices: [Int], checkoutType: CheckoutType) {
switch checkoutType {
case .normal:
finalPrice = getFinalPriceWithNormal(itemPrices: itemPrices)
case .discount:
finalPrice = getFinalPriceWithDiscount(itemPrices: itemPrices)
case .freeShipping:
finalPrice = getFinalPriceWithFreeShipping(itemPrices: itemPrices)
}
}
private func getFinalPriceWithNormal(itemPrices: [Int]) -> Int {
// do calculation
return 90 + 75 + 20
}
private func getFinalPriceWithDiscount(itemPrices: [Int]) -> Int {
// do calculation
return Int((90 + 75) * 0.9) + 20
}
private func getFinalPriceWithFreeShipping(itemPrices: [Int]) -> Int {
// do calculation
return 90 + 75
}
複製代碼
上述代碼能夠根據不一樣枚舉類型計算總價,那爲何要用 strategy pattern ?
Strategy pattern 可讓代碼遵照開閉原則 (Open/Closed principle)。在面向對象編程領域中,開閉原則規定「軟件中的對象、類、模塊和函數等,對於擴展應該是開放的,但對於修改是封閉的」。這意味着,一個對象容許在不改變其源代碼的前提下變動它的行爲。該特性在產品化的環境中是特別有價值的。在這種環境中,改變源代碼須要代碼審查、單元測試等以確保產品質量。遵循這種原則的代碼在擴展時並不發生變化,所以,無需上述過程。
目前,在TotalPriceViewController.swift
文件添加了enum
和所需方法,若是增長告終算方式,就須要更改TotalPriceViewController.swift
文件中代碼。但在TotalPriceViewController.swift
文件中增長enum
類型和switch
,就違背了 Open/Closed principle (open for extension and close for modification) 。同時TotalPriceViewController
掌握了太多其沒必要知道的內容,會使代碼高度耦合、難以測試。
添加CheckoutStrategy.swift
文件,定義一項協議。在咱們的示例中,該協議只有一個方法。
protocol CheckoutStrategy: class {
func getFinalPrice(with itemPrices:[Int]) -> Int
}
複製代碼
建立三種策略,分別執行正常價格、九折和免運費結算模式:
class NormalPriceStrategy: CheckoutStrategy {
func getFinalPrice(with itemPrices: [Int]) -> Int {
// do calculation
return 90 + 75 + 20
}
}
class DiscountStrategy: CheckoutStrategy {
func getFinalPrice(with itemPrices: [Int]) -> Int {
// do calculation
return Int((90 + 75) * 0.9) + 20
}
}
class FreeShippingStrategy: CheckoutStrategy {
func getFinalPrice(with itemPrices: [Int]) -> Int {
// do calculation
return 90 + 75
}
}
複製代碼
能夠看到,每一個類均遵照CheckoutStrategy
協議,但協議中方法實現方式有所不一樣,每一個類表明一種策略。
更新ShoppingCartViewController.swift
文件中prepare(for:sender:)
方法,使用 strategy pattern 結算:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
view.endEditing(false)
if let destinationViewController: TotalPriceViewController = segue.destination as? TotalPriceViewController {
destinationViewController.bindProperties(itemPrices: itemPrices, checkoutStrategy: NormalPriceStrategy())
// destinationViewController.bindProperties(itemPrices: itemPrices, checkoutStrategy: DiscountStrategy())
// destinationViewController.bindProperties(itemPrices: itemPrices, checkoutStrategy: FreeShippingStrategy())
}
}
複製代碼
同時在TotalPriceViewController.swift
文件添加如下方法:
public func bindProperties(itemPrices:[Int], checkoutStrategy: CheckoutStrategy) {
finalPrice = checkoutStrategy.getFinalPrice(with: itemPrices)
}
複製代碼
最後,project navigator 目錄以下:
如今,添加、修改結算模式時,只須要增長遵照CheckoutStrategy
協議的策略,修改結算策略便可。
下面示例也適合使用 strategy pattern 。
旅行 app 提供單程、往返兩種購票模式。當進入日曆時,根據單程、往返來決定採起哪一種選擇日期策略。例如,若是是往返,則返回日期必須晚於出發日期。
所以,咱們有兩種選擇策略,當打開日曆時指定所採用的策略:protocol CalendarSelectionStrategy {
func calendar(_ calendar: CalendarView, didSelect date: Date)
}
class OneWaySelectionStrategy: CalendarSelectionStrategy {
func calendar(_ calendar: CalendarView, didSelect date: Date) {
// One way selection logic
}
}
class ReturnWaySelectionStrategy: CalendarSelectionStrategy {
func calendar(_ calendar: CalendarView, didSelect date: Date) {
// Return selection logic
}
}
// Use
showCalendar(usingSelectionStrategy: OneWaySelectionStrategy())
複製代碼
App 內表格內容有多種文本類型,如文字、數字、密碼、手機號碼等,每種類型有不一樣驗證方式。咱們能夠爲每種類型定義一種驗證策略,這樣每一個文本框就能知道如何驗證其內容是否符合要求。
protocol ValidateStrategy {
func validate() -> Bool
}
複製代碼
在遊戲中,用戶會常常更換武器。武器的觸發操做方式是同樣的,但每種武器都有本身的射擊方式。用戶無需用知道具體射擊細節,武器自己知道便可。
你已經學過了 strategy pattern ,如下是其關鍵點:
當用不一樣的策略作不一樣的事情時,原來類將變得更簡潔。當用 strategy 擴展類時,無需更新採用 strategy 的類,遵照了 Open/Close principle 。
Demo名稱:StrategyPattern
轉載於做者:pro648