設計模式(Swift) - 2.單例模式、備忘錄模式和策略模式

上一篇 設計模式(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

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屡贺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甩栈,老刑警劉巖泻仙,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異量没,居然都是意外死亡玉转,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門殴蹄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來究抓,“玉大人,你說我怎么就攤上這事袭灯′鲶。” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵妓蛮,是天一觀的道長。 經(jīng)常有香客問我圾叼,道長蛤克,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任夷蚊,我火速辦了婚禮构挤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惕鼓。我一直安慰自己筋现,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布箱歧。 她就那樣靜靜地躺著矾飞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呀邢。 梳的紋絲不亂的頭發(fā)上洒沦,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音价淌,去河邊找鬼申眼。 笑死,一個胖子當著我的面吹牛蝉衣,可吹牛的內(nèi)容都是我干的括尸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼病毡,長吁一口氣:“原來是場噩夢啊……” “哼濒翻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肴焊,失蹤者是張志新(化名)和其女友劉穎前联,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娶眷,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡似嗤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了届宠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烁落。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖豌注,靈堂內(nèi)的尸體忽然破棺而出伤塌,到底是詐尸還是另有隱情,我是刑警寧澤轧铁,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布每聪,位于F島的核電站,受9級特大地震影響齿风,放射性物質(zhì)發(fā)生泄漏药薯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一救斑、第九天 我趴在偏房一處隱蔽的房頂上張望童本。 院中可真熱鬧,春花似錦脸候、人聲如沸穷娱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泵额。三九已至,卻和暖如春茶袒,著一層夾襖步出監(jiān)牢的瞬間梯刚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工薪寓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留亡资,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓向叉,卻偏偏與公主長得像锥腻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子母谎,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容