/ 设计模式 / 策略模式 / Swift Swift 策略模式讲解和代码示例 策略是一种行为设计模式, 它将一组行为转换为对象, 并使其在原始上下文对象内部能够相互替换。 原始对象被称为上下文, 它包含指向策略对象的引用并将执行行为的任务分派给策略对象。 为了改变上下文完成其工作的方式, 其他对象可以使用另一个对象来替换当前链接的策略对象。 进一步了解策略模式 导航 简介 概念示例 Example Output 真实世界示例 Example Output 复杂度: 流行度: 使用示例: 策略模式在 Swift 代码中很常见。 它经常在各种框架中使用, 能在不扩展类的情况下向用户提供改变其行为的方式。 识别方法: 策略模式可以通过允许嵌套对象完成实际工作的方法以及允许将该对象替换为不同对象的设置器来识别。 以下示例可在 Swift Playgrounds 上使用。 感谢 Alejandro Mohamad 创建了Playground版本。 概念示例 真实世界示例 概念示例 本例说明了策略设计模式的结构并重点回答了下面的问题: 它由哪些类组成? 这些类扮演了哪些角色? 模式中的各个元素会以何种方式相互关联? 了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 Swift 应用案例。 Example.swift: 概念示例 import XCTest /// The Context defines the interface of interest to clients. class Context { /// The Context maintains a reference to one of the Strategy objects. The /// Context does not know the concrete class of a strategy. It should work /// with all strategies via the Strategy interface. private var strategy: Strategy /// Usually, the Context accepts a strategy through the constructor, but /// also provides a setter to change it at runtime. init(strategy: Strategy) { self.strategy = strategy } /// Usually, the Context allows replacing a Strategy object at runtime. func update(strategy: Strategy) { self.strategy = strategy } /// The Context delegates some work to the Strategy object instead of /// implementing multiple versions of the algorithm on its own. func doSomeBusinessLogic() { print("Context: Sorting data using the strategy (not sure how it'll do it)\n") let result = strategy.doAlgorithm(["a", "b", "c", "d", "e"]) print(result.joined(separator: ",")) } } /// The Strategy interface declares operations common to all supported versions /// of some algorithm. /// /// The Context uses this interface to call the algorithm defined by Concrete /// Strategies. protocol Strategy { func doAlgorithm<T: Comparable>(_ data: [T]) -> [T] } /// Concrete Strategies implement the algorithm while following the base /// Strategy interface. The interface makes them interchangeable in the Context. class ConcreteStrategyA: Strategy { func doAlgorithm<T: Comparable>(_ data: [T]) -> [T] { return data.sorted() } } class ConcreteStrategyB: Strategy { func doAlgorithm<T: Comparable>(_ data: [T]) -> [T] { return data.sorted(by: >) } } /// Let's see how it all works together. class StrategyConceptual: XCTestCase { func test() { /// The client code picks a concrete strategy and passes it to the /// context. The client should be aware of the differences between /// strategies in order to make the right choice. let context = Context(strategy: ConcreteStrategyA()) print("Client: Strategy is set to normal sorting.\n") context.doSomeBusinessLogic() print("\nClient: Strategy is set to reverse sorting.\n") context.update(strategy: ConcreteStrategyB()) context.doSomeBusinessLogic() } } Output.txt: 执行结果 Client: Strategy is set to normal sorting. Context: Sorting data using the strategy (not sure how it'll do it) a,b,c,d,e Client: Strategy is set to reverse sorting. Context: Sorting data using the strategy (not sure how it'll do it) e,d,c,b,a 真实世界示例 Example.swift: 真实世界示例 import XCTest class StrategyRealWorld: XCTestCase { /// This example shows a simple implementation of a list controller that is /// able to display models from different data sources: /// /// (MemoryStorage, CoreDataStorage, RealmStorage) func test() { let controller = ListController() let memoryStorage = MemoryStorage<User>() memoryStorage.add(usersFromNetwork()) clientCode(use: controller, with: memoryStorage) clientCode(use: controller, with: CoreDataStorage()) clientCode(use: controller, with: RealmStorage()) } func clientCode(use controller: ListController, with dataSource: DataSource) { controller.update(dataSource: dataSource) controller.displayModels() } private func usersFromNetwork() -> [User] { let firstUser = User(id: 1, username: "username1") let secondUser = User(id: 2, username: "username2") return [firstUser, secondUser] } } class ListController { private var dataSource: DataSource? func update(dataSource: DataSource) { /// ... resest current states ... self.dataSource = dataSource } func displayModels() { guard let dataSource = dataSource else { return } let models = dataSource.loadModels() as [User] /// Bind models to cells of a list view... print("\nListController: Displaying models...") models.forEach({ print($0) }) } } protocol DataSource { func loadModels<T: DomainModel>() -> [T] } class MemoryStorage<Model>: DataSource { private lazy var items = [Model]() func add(_ items: [Model]) { self.items.append(contentsOf: items) } func loadModels<T: DomainModel>() -> [T] { guard T.self == User.self else { return [] } return items as! [T] } } class CoreDataStorage: DataSource { func loadModels<T: DomainModel>() -> [T] { guard T.self == User.self else { return [] } let firstUser = User(id: 3, username: "username3") let secondUser = User(id: 4, username: "username4") return [firstUser, secondUser] as! [T] } } class RealmStorage: DataSource { func loadModels<T: DomainModel>() -> [T] { guard T.self == User.self else { return [] } let firstUser = User(id: 5, username: "username5") let secondUser = User(id: 6, username: "username6") return [firstUser, secondUser] as! [T] } } protocol DomainModel { var id: Int { get } } struct User: DomainModel { var id: Int var username: String } Output.txt: 执行结果 ListController: Displaying models... User(id: 1, username: "username1") User(id: 2, username: "username2") ListController: Displaying models... User(id: 3, username: "username3") User(id: 4, username: "username4") ListController: Displaying models... User(id: 5, username: "username5") User(id: 6, username: "username6") 概念示例 真实世界示例