Factory Method em Swift
O Factory method é um padrão de projeto criacional, que resolve o problema de criar objetos de produtos sem especificar suas classes concretas.
O Factory Method define um método, que deve ser usado para criar objetos em vez da chamada direta ao construtor (operador new). As subclasses podem substituir esse método para alterar a classe de objetos que serão criados.
Se você não conseguir descobrir a diferença entre os padrões Factory, Factory Method e Abstract Factory, leia nossa Comparação Factory.
Complexidade:
Popularidade:
Exemplos de uso: O padrão Factory Method é amplamente utilizado no código Swift. É muito útil quando você precisa fornecer um alto nível de flexibilidade para seu código.
Identificação: Os métodos fábrica podem ser reconhecidos por métodos de criação, que criam objetos de classes concretas, mas os retornam como objetos de tipo ou interface abstrata.
Exemplo conceitual
Este exemplo ilustra a estrutura do padrão de projeto Factory Method. Ele se concentra em responder a estas perguntas:
- De quais classes ele consiste?
- Quais papéis essas classes desempenham?
- De que maneira os elementos do padrão estão relacionados?
Depois de aprender sobre a estrutura do padrão, será mais fácil entender o exemplo a seguir, com base em um caso de uso Swift do mundo real.
Example.swift: Exemplo conceitual
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: Resultados da execução
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} Exemplo do mundo real
Example.swift: Exemplo do mundo real
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 whether the client code is already presenting something... 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: Resultados da execução
Info is presented over Wifi: Very important info of the presentation Info is presented over Bluetooth: Very important info of the presentation