/ 设计模式 / 享元模式 / Swift Swift 享元模式讲解和代码示例 享元是一种结构型设计模式, 它允许你在消耗少量内存的情况下支持大量对象。 模式通过共享多个对象的部分状态来实现上述功能。 换句话来说, 享元会将不同对象的相同数据进行缓存以节省内存。 进一步了解享元模式 导航 简介 概念示例 Example Output 真实世界示例 Example Output 复杂度: 流行度: 使用示例: 享元模式只有一个目的: 将内存消耗最小化。 如果你的程序没有遇到内存容量不足的问题, 则可以暂时忽略该模式。 识别方法: 享元可以通过构建方法来识别, 它会返回缓存对象而不是创建新的对象。 以下示例可在 Swift Playgrounds 上使用。 感谢 Alejandro Mohamad 创建了Playground版本。 概念示例 真实世界示例 概念示例 本例说明了享元设计模式的结构并重点回答了下面的问题: 它由哪些类组成? 这些类扮演了哪些角色? 模式中的各个元素会以何种方式相互关联? 了解该模式的结构后, 你可以更轻松地理解下面基于真实世界的 Swift 应用案例。 Example.swift: 概念示例 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]) } } Output.txt: 执行结果 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 真实世界示例 Example.swift: 真实世界示例 import XCTest import UIKit class FlyweightRealWorld: XCTestCase { func testFlyweightRealWorld() { let maineCoon = Animal(name: "Maine Coon", country: "USA", type: .cat) let sphynx = Animal(name: "Sphynx", country: "Egypt", type: .cat) let bulldog = Animal(name: "Bulldog", country: "England", type: .dog) print("Client: I created a number of objects to display") /// Displaying objects for the 1-st time. print("Client: Let's show animals for the 1st time\n") display(animals: [maineCoon, sphynx, bulldog]) /// Displaying objects for the 2-nd time. /// /// Note: Cached object of the appearance will be reused this time. print("\nClient: I have a new dog, let's show it the same way!\n") let germanShepherd = Animal(name: "German Shepherd", country: "Germany", type: .dog) display(animals: [germanShepherd]) } } extension FlyweightRealWorld { func display(animals: [Animal]) { let cells = loadCells(count: animals.count) for index in 0..<animals.count { cells[index].update(with: animals[index]) } /// Using cells... } func loadCells(count: Int) -> [Cell] { /// Emulates behavior of a table/collection view. return Array(repeating: Cell(), count: count) } } enum Type: String { case cat case dog } class Cell { private var animal: Animal? func update(with animal: Animal) { self.animal = animal let type = animal.type.rawValue let photos = "photos \(animal.appearance.photos.count)" print("Cell: Updating an appearance of a \(type)-cell: \(photos)\n") } } struct Animal: Equatable { /// This is an external context that contains specific values and an object /// with a common state. /// /// Note: The object of appearance will be lazily created when it is needed let name: String let country: String let type: Type var appearance: Appearance { return AppearanceFactory.appearance(for: type) } } struct Appearance: Equatable { /// This object contains a predefined appearance of every cell let photos: [UIImage] let backgroundColor: UIColor } extension Animal: CustomStringConvertible { var description: String { return "\(name), \(country), \(type.rawValue) + \(appearance.description)" } } extension Appearance: CustomStringConvertible { var description: String { return "photos: \(photos.count), \(backgroundColor)" } } class AppearanceFactory { private static var cache = [Type: Appearance]() static func appearance(for key: Type) -> Appearance { guard cache[key] == nil else { print("AppearanceFactory: Reusing an existing \(key.rawValue)-appearance.") return cache[key]! } print("AppearanceFactory: Can't find a cached \(key.rawValue)-object, creating a new one.") switch key { case .cat: cache[key] = catInfo case .dog: cache[key] = dogInfo } return cache[key]! } } extension AppearanceFactory { private static var catInfo: Appearance { return Appearance(photos: [UIImage()], backgroundColor: .red) } private static var dogInfo: Appearance { return Appearance(photos: [UIImage(), UIImage()], backgroundColor: .blue) } } Output.txt: 执行结果 Client: I created a number of objects to display Client: Let's show animals for the 1st time AppearanceFactory: Can't find a cached cat-object, creating a new one. Cell: Updating an appearance of a cat-cell: photos 1 AppearanceFactory: Reusing an existing cat-appearance. Cell: Updating an appearance of a cat-cell: photos 1 AppearanceFactory: Can't find a cached dog-object, creating a new one. Cell: Updating an appearance of a dog-cell: photos 2 Client: I have a new dog, let's show it the same way! AppearanceFactory: Reusing an existing dog-appearance. Cell: Updating an appearance of a dog-cell: photos 2 概念示例 真实世界示例