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

作者: YxxxHao | 来源:发表于2017-07-26 23:52 被阅读143次

    什么是享元模式

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

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

    标准组成

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

    结构

    图片.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<String, String> = 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)

        本文链接:https://www.haomeiwen.com/subject/opsakxtx.html