/ 设计模式 / 工厂方法模式 / Swift Swift 工厂方法模式讲解和代码示例 工厂方法是一种创建型设计模式, 解决了在不指定具体类的情况下创建产品对象的问题。 工厂方法定义了一个方法, 且必须使用该方法代替通过直接调用构造函数来创建对象 ( new操作符) 的方式。 子类可重写该方法来更改将被创建的对象所属类。 如果你不清楚工厂、 工厂方法和抽象工厂模式之间的区别, 请参阅工厂模式比较。 进一步了解工厂方法模式 导航 简介 概念示例 Example Output 真实世界示例 Example Output 复杂度: 流行度: 使用示例: 工厂方法模式在 Swift 代码中得到了广泛使用。 当你需要在代码中提供高层次的灵活性时, 该模式会非常实用。 识别方法: 工厂方法可通过构建方法来识别, 它会创建具体类的对象, 但以抽象类型或接口的形式返回这些对象。 以下示例可在 Swift Playgrounds 上使用。 感谢 Alejandro Mohamad 创建了Playground版本。 概念示例 真实世界示例 概念示例 本例说明了工厂方法设计模式的结构并重点回答了下面的问题: 它由哪些类组成? 这些类扮演了哪些角色? 模式中的各个元素会以何种方式相互关联? 在了解该模式的结构后, 你可以更容易地理解下面的基于真实世界的 Swift 应用案例。 Example.swift: 概念示例 import XCTest /// The Creator protocol declares the factory method that's supposed to return a /// new object of a Product class. The Creator's subclasses usually provide the /// implementation of this method. protocol Creator { /// Note that the Creator may also provide some default implementation of /// the factory method. func factoryMethod() -> Product /// Also note that, despite its name, the Creator's primary responsibility /// is not creating products. Usually, it contains some core business logic /// that relies on Product objects, returned by the factory method. /// Subclasses can indirectly change that business logic by overriding the /// factory method and returning a different type of product from it. func someOperation() -> String } /// This extension implements the default behavior of the Creator. This behavior /// can be overridden in subclasses. extension Creator { func someOperation() -> String { // Call the factory method to create a Product object. let product = factoryMethod() // Now, use the product. return "Creator: The same creator's code has just worked with " + product.operation() } } /// Concrete Creators override the factory method in order to change the /// resulting product's type. class ConcreteCreator1: Creator { /// Note that the signature of the method still uses the abstract product /// type, even though the concrete product is actually returned from the /// method. This way the Creator can stay independent of concrete product /// classes. public func factoryMethod() -> Product { return ConcreteProduct1() } } class ConcreteCreator2: Creator { public func factoryMethod() -> Product { return ConcreteProduct2() } } /// The Product protocol declares the operations that all concrete products must /// implement. protocol Product { func operation() -> String } /// Concrete Products provide various implementations of the Product protocol. class ConcreteProduct1: Product { func operation() -> String { return "{Result of the ConcreteProduct1}" } } class ConcreteProduct2: Product { func operation() -> String { return "{Result of the ConcreteProduct2}" } } /// The client code works with an instance of a concrete creator, albeit through /// its base protocol. As long as the client keeps working with the creator via /// the base protocol, you can pass it any creator's subclass. class Client { // ... static func someClientCode(creator: Creator) { print("Client: I'm not aware of the creator's class, but it still works.\n" + creator.someOperation()) } // ... } /// Let's see how it all works together. class FactoryMethodConceptual: XCTestCase { func testFactoryMethodConceptual() { /// The Application picks a creator's type depending on the /// configuration or environment. print("App: Launched with the ConcreteCreator1.") Client.someClientCode(creator: ConcreteCreator1()) print("\nApp: Launched with the ConcreteCreator2.") Client.someClientCode(creator: ConcreteCreator2()) } } Output.txt: 执行结果 App: Launched with the ConcreteCreator1. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct1} App: Launched with the ConcreteCreator2. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct2} 真实世界示例 Example.swift: 真实世界示例 import XCTest class FactoryMethodRealWorld: XCTestCase { func testFactoryMethodRealWorld() { let info = "Very important info of the presentation" let clientCode = ClientCode() /// Present info over WiFi clientCode.present(info: info, with: WifiFactory()) /// Present info over Bluetooth clientCode.present(info: info, with: BluetoothFactory()) } } protocol ProjectorFactory { func createProjector() -> Projector func syncedProjector(with projector: Projector) -> Projector } extension ProjectorFactory { /// Base implementation of ProjectorFactory func syncedProjector(with projector: Projector) -> Projector { /// Every instance creates an own projector let newProjector = createProjector() /// sync projectors newProjector.sync(with: projector) return newProjector } } class WifiFactory: ProjectorFactory { func createProjector() -> Projector { return WifiProjector() } } class BluetoothFactory: ProjectorFactory { func createProjector() -> Projector { return BluetoothProjector() } } protocol Projector { /// Abstract projector interface var currentPage: Int { get } func present(info: String) func sync(with projector: Projector) func update(with page: Int) } extension Projector { /// Base implementation of Projector methods func sync(with projector: Projector) { projector.update(with: currentPage) } } class WifiProjector: Projector { var currentPage = 0 func present(info: String) { print("Info is presented over Wifi: \(info)") } func update(with page: Int) { /// ... scroll page via WiFi connection /// ... currentPage = page } } class BluetoothProjector: Projector { var currentPage = 0 func present(info: String) { print("Info is presented over Bluetooth: \(info)") } func update(with page: Int) { /// ... scroll page via Bluetooth connection /// ... currentPage = page } } private class ClientCode { private var currentProjector: Projector? func present(info: String, with factory: ProjectorFactory) { /// Check wheater a client code already present smth... guard let projector = currentProjector else { /// 'currentProjector' variable is nil. Create a new projector and /// start presentation. let projector = factory.createProjector() projector.present(info: info) self.currentProjector = projector return } /// Client code already has a projector. Let's sync pages of the old /// projector with a new one. self.currentProjector = factory.syncedProjector(with: projector) self.currentProjector?.present(info: info) } } Output.txt: 执行结果 Info is presented over Wifi: Very important info of the presentation Info is presented over Bluetooth: Very important info of the presentation 概念示例 真实世界示例