iOS&设计模式 - 享元模式(Flyweight)

什么是享元模式

运用共享技术有效地支持大量细粒度的对象(注:择自设计模式黑书系列)

这概念看得真是让人一脸懵逼,举个简单的例子吧,比如共享单车,一台共享单车可以服务多个人,前一个用户使用完后可以交给下一个用户使用,用户不需要单独买一辆自行车,共享单车就是上面所说的共享对象,而用户则是上面所说的细粒度对象。不但共享单车,现实生活中的公共设施都可以说成是一种享元(分享元素)模式。

标准组成

  • Flyweight:享元中的抽象接口,可以接受并作用于外部状态
  • ConcreteFlyweight:实现享元接口,该对象必须是可以共享的
  • UnshareConcreteFlyweight:非共享对象,Flyweight 接口使共享成为可能,但并不强制共享
  • FlyweightFactory:创建并管理 flyweight

结构

iOS&设计模式 - 享元模式(Flyweight)_第1张图片
图片.png

实现

下面以共享单车 ofo 为例,实现一个享元模式:

Flyweight:

import Cocoa

protocol OFOProtocol {
    
    var id: String { get }
    var isUse: Bool { get }
    
    func useOFO()
    func endUseOFO()
}

ConcreteFlyweight:

import Cocoa

class OFO: OFOProtocol {
    
    var id: String
    var isUse: Bool = false
    
    init(id: String) {
        self.id = id
    }
    
    func useOFO() {
        isUse = true
        print("use ofo")
    }
    
    func endUseOFO() {
        isUse = false
    }
}

FlyweightFactory:

import Cocoa

class OFOFactory: NSObject {
    static var ofoCache: [String : OFOProtocol] = [:]
    
    static func useOFO(id: String) -> OFOProtocol? {
        guard let ofo = ofoCache[id] else {
            let ofo = OFO(id: id)
            ofo.useOFO()
            ofoCache[id] = ofo
            return ofo
        }
        if ofo.isUse {
            print("using!")
            return nil
        }
        ofo.useOFO()
        ofoCache[id] = ofo
        return ofo
    }
}

Main 方法:

import Foundation

print(OFOFactory.useOFO(id: "1")?.id ?? "ofo using")

for _ in 0..<10 {
    print(OFOFactory.useOFO(id: "1")?.id ?? "ofo using")
}
for _ in 0..<10 {
    print(OFOFactory.useOFO(id: "2")?.id ?? "ofo using")
}

非标准例子

最近在写开源的聊天应用,其中的草稿功能用到了享元模式,但这里并非是完全按标准走的,没有 Flyweight,但却不影响它是一个享元模式,先上代码后解析为什么用享元模式:

class JCDraft: NSObject {
    
    static var draftCache: Dictionary = Dictionary()
    
    static func update(text: String?, conversation: JMSGConversation) {
        let id = JCDraft.getDraftId(conversation)
        if text == nil || (text?.isEmpty)! {
            UserDefaults.standard.removeObject(forKey: id)
            draftCache.removeValue(forKey: id)
            return
        }
        UserDefaults.standard.set(text!, forKey: id)
        draftCache[id] = text!
    }
    
    static func getDraft(_ conversation: JMSGConversation) -> String? {
        let id = JCDraft.getDraftId(conversation)
        if let cache = draftCache[id] {
            return cache
        }
        let draft = UserDefaults.standard.object(forKey: id) as? String
        if draft != nil {
            draftCache[id] = draft
        } else {
            draftCache[id] = ""
        }
        return draft
    }
    
    static func getDraftId(_ conversation: JMSGConversation) -> String {
        var id = ""
        // get id
        return id
    }
}

上面的 UserDefaults.standard.object(forKey: id) 其实就是充当了享元模式中的 ConcreteFlyweight。

为什么要用享元呢,场景是这样的,读取聊天会话消息时,同时读取本地是不保存有该会话的草稿消息,如果有大量消息下发时,会话就会去刷新并同时重新获取草稿消息,如果不用享元模式,那么同一个会话的多次刷新就会多次访问数据库去读取,大量读取数据库是会很影响性能的,这时通过享元就能很好解决这个问题了。

小结

享元模式在更新操作的时候会带来额外的开销,但是在空间上会节省开销,至于是否需要使用享元模式,就得通过衡量这两方面的消耗来选择了。

iOS&设计模式 - 享元模式(Flyweight)_第2张图片
关注公众号

你可能感兴趣的:(iOS&设计模式 - 享元模式(Flyweight))