iOS设计模式之享元模式

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情

  • 本文主要介绍iOS设计模式中的享元模式,享元模式顾名思义就是通过共享元素来节约内存,节省储存空间

1. 什么是享元模式

比如我们的公共交通已经有一百多年历史了,大量去往相同方向的乘客可以分担车辆的费用。公共交通设有多个车站。乘客沿者路线在接近他们目的地的地方上下车。到达目的地的费用仅与路程有关。跟保有车辆相比,乘坐公共交通要便宜很多。这就是利用公共资源的好处。

假如你希望在长时间工作后放松一下, 所以开发了一款简单的游戏: 玩家们在地图上移动并相互射击。 你决定实现一个真实的粒子系统, 并将其作为游戏的特色。 大量的子弹、 导弹和爆炸弹片会在整个地图上穿行, 为玩家提供紧张刺激的游戏体验。对于这些子弹的对象他们外表基本相似,只是位置不同。这个时候我们共享几个子弹的样式,则会大量减少我们持有的对象数量减少内存消耗

我们经常使用的打车软件,地图上会显示很多车辆,可以发现车型就那么几种只是车辆的状态或者位置不同,如果软件地图上有大量的车辆对象,势必造成卡顿,影响用户体验,这时候使用享元模式。通过一个缓存池保存我们每一种车辆对象,外部计算车辆位置等状态。根据数据从缓存池子中取出我们要的对象,添加状态

实现享元模式要2个关键组件,通常是可共享的享元对象保存他们的池。某种中央对象维护这个池,并从它返回适当的实例。工厂是这一角色的理想候选。它可以通过一个工厂方法,根据父类返回各种类型的具体享元对象。各种框架中,这种工厂通常称为“管理器”,主要目的就是维护池中的享元对象,并适当地从中返回享元对象。

享元模式:运用共享技术有效地支持大量细粒度的对象。

2. 什么时候使用享元模式

以下情况可以考虑使用享元模式

  • 应用程序使用很多对象
  • 在内存中保存对象会影响内存性能
  • 对象的多数特有状态(外在状态)可以放到外部而轻量化
  • 移除了外在状态后,可以用较少的共享对象替代原来的那组对象。
  • 应用 不依赖于对象标识,因为共享对象不能提供唯一的标识

3. 代码表示

import XCTest

/// The Flyweight stores a common portion of the state (also called intrinsic
/// state) that belongs to multiple real business entities. The Flyweight
/// accepts the rest of the state (extrinsic state, unique for each entity) via
/// its method parameters.
class Flyweight {

    private let sharedState: [String]

    init(sharedState: [String]) {
        self.sharedState = sharedState
    }

    func operation(uniqueState: [String]) {
        print("Flyweight: Displaying shared ((sharedState)) and unique ((uniqueState) state.\n")
    }
}

/// The Flyweight Factory creates and manages the Flyweight objects. It ensures
/// that flyweights are shared correctly. When the client requests a flyweight,
/// the factory either returns an existing instance or creates a new one, if it
/// doesn't exist yet.
class FlyweightFactory {

    private var flyweights: [String: Flyweight]

    init(states: [[String]]) {

        var flyweights = [String: Flyweight]()

        for state in states {
            flyweights[state.key] = Flyweight(sharedState: state)
        }

        self.flyweights = flyweights
    }

    /// Returns an existing Flyweight with a given state or creates a new one.
    func flyweight(for state: [String]) -> Flyweight {

        let key = state.key

        guard let foundFlyweight = flyweights[key] else {

            print("FlyweightFactory: Can't find a flyweight, creating new one.\n")
            let flyweight = Flyweight(sharedState: state)
            flyweights.updateValue(flyweight, forKey: key)
            return flyweight
        }
        print("FlyweightFactory: Reusing existing flyweight.\n")
        return foundFlyweight
    }

    func printFlyweights() {
        print("FlyweightFactory: I have (flyweights.count) flyweights:\n")
        for item in flyweights {
            print(item.key)
        }
    }
}

extension Array where Element == String {

    /// Returns a Flyweight's string hash for a given state.
    var key: String {
        return self.joined()
    }
}


class FlyweightConceptual: XCTestCase {

    func testFlyweight() {

        /// The client code usually creates a bunch of pre-populated flyweights
        /// in the initialization stage of the application.

        let factory = FlyweightFactory(states:
        [
            ["Chevrolet", "Camaro2018", "pink"],
            ["Mercedes Benz", "C300", "black"],
            ["Mercedes Benz", "C500", "red"],
            ["BMW", "M5", "red"],
            ["BMW", "X6", "white"]
        ])

        factory.printFlyweights()

        /// ...

        addCarToPoliceDatabase(factory,
                "CL234IR",
                "James Doe",
                "BMW",
                "M5",
                "red")

        addCarToPoliceDatabase(factory,
                "CL234IR",
                "James Doe",
                "BMW",
                "X1",
                "red")

        factory.printFlyweights()
    }

    func addCarToPoliceDatabase(
            _ factory: FlyweightFactory,
            _ plates: String,
            _ owner: String,
            _ brand: String,
            _ model: String,
            _ color: String) {

        print("Client: Adding a car to database.\n")

        let flyweight = factory.flyweight(for: [brand, model, color])

        /// The client code either stores or calculates extrinsic state and
        /// passes it to the flyweight's methods.
        flyweight.operation(uniqueState: [plates, owner])
    }
}
复制代码

执行结果

FlyweightFactory: I have 5 flyweights:

Mercedes BenzC500red
ChevroletCamaro2018pink
Mercedes BenzC300black
BMWX6white
BMWM5red
Client: Adding a car to database.

FlyweightFactory: Reusing existing flyweight.

Flyweight: Displaying shared (["BMW", "M5", "red"]) and unique (["CL234IR", "James Doe"] state.

Client: Adding a car to database.

FlyweightFactory: Can't find a flyweight, creating new one.

Flyweight: Displaying shared (["BMW", "X1", "red"]) and unique (["CL234IR", "James Doe"] state.

FlyweightFactory: I have 6 flyweights:

Mercedes BenzC500red
BMWX1red
ChevroletCamaro2018pink
Mercedes BenzC300black
BMWX6white
BMWM5red
复制代码

4. 小结

享元对象可以节约空间主要是减少对象总数,每个对象可共享的状态的数量,外在状态是计算出来还是保存的。共享减少了内在状态的开销,通过牺牲计算时间又节省了外在状态存储空间。分享相同的资源以执行任务,可能比使用个人的资源完成同样的事情更加高效

猜你喜欢

转载自juejin.im/post/7110220799230869540