上一篇 設計模式(Swift) - 1.MVC和代理 中涉及到了三點,類圖,MVC和代理模式.
- 類圖用來清晰直觀的表達設計模式.
- 作為Cocoa框架的核心結(jié)構(gòu)模式,MVC怎樣處理視圖莲蜘、模型、控制器之間的關系.
- 將我想做的事委托給有能力的人的代理模式.
1. 單例模式(Singleton Pattern)
1.單例概述
單例限制了類的實例化,一個類只能實例化一個對象,所有對單例對象的引用都是指向了同一個對象.
2.單例的使用
// 定義一個單例
final public class MySingleton {
static let shared = MySingleton()
private init() {} // 私有化構(gòu)造方法(如果有需要也可以去掉)
}
// 使用
let s1 = MySingleton.shared
let s2 = MySingleton.shared
// let s3 = MySingleton() // 報錯
dc.address(o: s1) // 0x000060400000e5e0
dc.address(o: s2) // 0x000060400000e5e0
相比OC,swift中單例的實現(xiàn)簡化了不少,swift中可以使用let這種方式來保證線程安全.
3.單例使用的優(yōu)缺點
- 優(yōu)點:
1.由于單例能保證一個對象存在,我們可以對某些操作進行統(tǒng)一管理,比如,app開發(fā)中用戶信息的保存和讀取,如后臺開發(fā)中服務器配置信息的管理操作.這樣可以有效的避免不必要的對象的創(chuàng)建和銷毀,提高開發(fā)效率.
- 缺點:
1. 單例模式本質(zhì)上延長了對象的聲明周期,如果強引用了比較大的對象,不可避免的會造成內(nèi)存問題,所以在適當?shù)臅r候需要進行重置單例的操作.
2. 由于單例是全局共享,也就意味著整個項目中都可以對單例進行操作,使得出現(xiàn)問題比較難定位.
2. 備忘錄模式(Memento Pattern)
1.備忘錄模式概述
通過備忘錄模式我們可以把某個對象保存在本地,并在適當?shù)臅r候恢復出來.
備忘錄模式總體來說分為三部分:
- 發(fā)起人(Originator): 負責創(chuàng)建一個備忘錄對象意乓,用以保存當前的狀態(tài)拆檬,并可使用備忘錄恢復內(nèi)部狀態(tài)赏迟。
- Memento(備忘錄): 負責存儲Originator對象,在swift中由Codable實現(xiàn).
- Caretaker(管理者): 負責備忘錄的保存與恢復工作.
Swift tips: Codable
Codable是swift4推出來的新特性,所有基本類型都實現(xiàn)了 Codable 協(xié)議,只要自定義的對象遵守了該協(xié)議,就可以保存和恢復所需要的對象.
本質(zhì)上Codable,就是Decodable和Encodable的集合.
具體拓展可以看這里Swift 4 踩坑之 Codable 協(xié)議
public typealias Codable = Decodable & Encodable
2.備忘錄模式舉例
個人用戶信息的本地化存儲,包括用戶token啊之類的.
1.個人信息操作的業(yè)務邏輯:
// MARK: - Originator(發(fā)起人)
public class UserInfo: Codable {
static let shared = UserInfo()
private init() {}
public var isLogin: Bool = false
public var account: String?
public var age: Int?
var description: String {
return "account:\(account ?? "為空"), age:\(age ?? 0)"
}
}
// MARK: - 備忘錄(Memento): 負責存儲Originator對象,swift中由Codable實現(xiàn)
// MARK: - 管理者(CareTaker)
public class UserInfoTaker {
public static let UserInforKey = "UserInfoKey"
private static let decoder = JSONDecoder()
private static let encoder = JSONEncoder()
private static let userDefaults = UserDefaults.standard
public static func save(_ p: UserInfo) throws {
let data = try encoder.encode(p)
userDefaults.set(data, forKey: UserInforKey)
}
public static func load() throws -> UserInfo {
guard let data = userDefaults.data(forKey: UserInforKey),
let userInfo = try? decoder.decode(UserInfo.self, from: data)
else {
throw Error.UserInfoNotFound
}
// decode生成的對象不是單例對象,需要轉(zhuǎn)換成單例對象
// 如果你有更好的實現(xiàn)方式歡迎交流
let userInfoS = UserInfo.shared
userInfoS.account = userInfo.account
userInfoS.age = userInfo.age
userInfoS.isLogin = userInfo.isLogin
return userInfoS
}
public enum Error: String, Swift.Error {
case UserInfoNotFound
}
}
2.個人信息操作:
let userInfo = UserInfo.shared
userInfo.isLogin = true
userInfo.account = "132154"
userInfo.age = 16
// 保存
do {
try UserInfoTaker.save(userInfo)
}catch {
print(error)
}
// 讀取
do {
let newUserInfo = try UserInfoTaker.load()
dc.log(newUserInfo.description) // account:132154, age:16
dc.address(o: newUserInfo) // 0x000060000009a400
}catch {
print(error)
}
dc.log(userInfo.description) // account:132154, age:16
dc.address(o: userInfo) // 0x000060000009a400
備忘錄的最大好處就是可以恢復到特定的狀態(tài),但每次的讀寫操作需要消耗一定的系統(tǒng)資源,所以在某些場景下可以將單例模式和備忘錄模式結(jié)合來統(tǒng)一管理操作數(shù)據(jù).
3. 策略模式(Strategy Pattern)
1.策略模式概述
在日常開發(fā)中,我們經(jīng)常會碰到邏輯分支,我們一般會用 if else或者switch去處理,但其實還有更好的方式: 策略模式.
策略模式抽象并封裝業(yè)務細節(jié),只給出相關的策略接口作為切換.
策略模式總體來說分為三部分:
- 策略模式的使用者: 為了統(tǒng)一直觀的使用策略模式,我們通常會用一個switch語句再做一層封裝.
- 策略協(xié)議: 抽象出策略對象需要實現(xiàn)的屬性,方法.
- 策略對象: 具體的業(yè)務邏輯實現(xiàn)者.
2.策略模式舉例
實現(xiàn)一個商場打折的例子,分為三種情況,原價購買,按照一個折扣購買,滿多少返現(xiàn)多少(滿100減20).
可以先思考下再看代碼.
1.實現(xiàn)商場打折的業(yè)務邏輯:
// 策略協(xié)議
protocol DiscountStrategy {
// 支付價格
func payment(money: Double) -> Double
}
// 原價購買
class DiscountNormal: DiscountStrategy {
func payment(money: Double) -> Double {
return money
}
}
// 打折
class DiscountRebate: DiscountStrategy {
private let rebate: Double // 折扣
init(rebate: Double) {
self.rebate = rebate
}
func payment(money: Double) -> Double {
return money * rebate/10.0
}
}
// 返現(xiàn)
class DiscountReturn: DiscountStrategy {
private let moneyCondition: Double // 滿
private let moneyReturn: Double // 返
init(moneyCondition: Double, moneyReturn: Double) {
self.moneyCondition = moneyCondition
self.moneyReturn = moneyReturn
}
func payment(money: Double) -> Double {
return money - (Double(Int(money/moneyCondition)) * moneyReturn)
}
}
// 策略枚舉
enum PayMentStyle {
case normal
case rebate(rebate: Double)
case `return`(moneyCondition: Double, moneyReturn: Double)
}
// 策略管理
class DiscountContext {
var discountStrategy: DiscountStrategy?
init(style: PayMentStyle) {
switch style { // 對應的三種方式
case .normal:
discountStrategy = DiscountNormal()
case .rebate(rebate: let money):
discountStrategy = DiscountRebate(rebate: money)
case .return(moneyCondition: let condition, moneyReturn: let `return`):
discountStrategy = DiscountReturn(moneyCondition: condition, moneyReturn: `return`)
}
}
func getResult(money: Double) -> Double {
return discountStrategy?.payment(money: money) ?? 0
}
}
2.使用:
let money: Double = 800
let normalPrice = DiscountContext(style: .normal).getResult(money: money)
let rebatePrice = DiscountContext(style: .rebate(rebate: 8)).getResult(money: money)
let returnPrice = DiscountContext(style: .return(moneyCondition: 100, moneyReturn: 20)).getResult(money: money)
print("正常價格:\(normalPrice)") // 正常價格:800.0
print("打八折:\(rebatePrice)") // 打八折:640.0
print("滿100返20:\(returnPrice)") // 滿100返20:640.0
以上就是一個簡單的策略模式實現(xiàn),通過DiscountContext來管理每一個DiscountStrategy.
4. 總結(jié)
主要講了三種模式,只能實例化一個對象的單例模式,可以存儲和恢復數(shù)據(jù)的備忘錄模式以及可以在復雜業(yè)務邏輯下替代if else和switch語句的策略模式.
參考:
The Swift Programming Language (Swift 4.1)
Objective-C編程之道
Design Patterns by Tutorials
如有疑問,歡迎留言 :-D