Abstract Factory Pattern in Swift
Learn how the abstract factory pattern allows you to improve you codebase making it more readable and maintainable.
• 3 min read
Abstract Factory is a pattern that allow you to create families of object that conform to a common protocol.
This patterns has three main components: the factory, the product and a client.
Factory:
- Factory protocol: Declares an interface that allows to create abstract objects.
- Concrete factory (class or struct): Implements the operations to create abstract objects.
Product:
- Product protocol: Declares an interface that defines a product object.
- Concrete product (class or struct): Implements the AbstractProduct interface.
Client:
- Client (class or struct): Uses only interfaces declared by Factory protocol and Product protocol.
Implementation
This example illustrates the structure of the Abstract Factory design pattern.
Products
We create a Product
protocol that defines a single product. And then we add the concrete classes that implements the Product protocol.
// Products
protocol Car {
func drive()
func canBeDriven(by driver: Driver) -> Bool
}
class ToyotaCar: Car {
func drive() {
print("Start drive Toyota")
}
func canBeDriven(by driver: Driver) -> Bool {
if driver is ProfessionalDriver {
return true
} else {
return false
}
}
}
class LamborghiniCar: Car {
func drive() {
print("Start drive Lamborghini")
}
func canBeDriven(by driver: Driver) -> Bool {
if driver is ProfessionalDriver {
return true
} else {
return false
}
}
}
protocol Driver {
var name: String { get set }
var surname: String { get set }
func whoAmI()
}
class ProfessionalDriver: Driver {
var name: String
var surname: String
init(name: String, surname: String) {
self.name = name
self.surname = surname
}
func whoAmI() {
print("I am a professional driver.")
}
}
class NewbieDriver: Driver {
var name: String
var surname: String
init(name: String, surname: String) {
self.name = name
self.surname = surname
}
func whoAmI() {
print("I am a newbie driver.")
}
}
Factories
We create a Factory
protocol that defines an interface for creating all distinct products. And then we add the concrete classes that implements the Factory protocol.
// Factories
protocol Factory {
func hireDriver() -> Driver
func buildCar() -> Car
}
class ToyotaFactory: Factory {
func hireDriver() -> Driver {
ProfessionalDriver(name: "Fernando", surname: "Alonso")
}
func buildCar() -> Car {
ToyotaCar()
}
}
class LamborghiniFactory: Factory {
func hireDriver() -> Driver {
NewbieDriver(name: "Jhon", surname: "Smith")
}
func buildCar() -> Car {
LamborghiniCar()
}
}
Client
Here we create the client that calls the creation methods of a factory object instead of creating products directly with a constructor call.
Tip
In the init
we used Dependency Injection that allows us to inject a factory in the client.
class Client {
let factory: Factory
var driverName: String {
let driver = self.hireDriver()
return "\(driver.name) \(driver.surname)"
}
// Inits
init(factory: Factory) {
self.factory = factory
}
// Methods
func buildCar() -> Car {
self.factory.buildCar()
}
func hireDriver() -> Driver {
self.factory.hireDriver()
}
func canBeDriven() -> Bool {
self.buildCar().canBeDriven(by: hireDriver())
}
}
Usage
Now we can instanciate the client providing a factory.
Conclusion
PROS
- Isolation of concrete classes. It helps to control the classes of objects created. Because a factory encapsulates the responsibility and process of creating objects and isolates clients from concrete classes.
- Objects of the same family can be easily interchanged. Each product of the same class can be swapped since they all conform to the same protocol.
- Promotes consistency between objects of the same family. Each object of the same family has equal methods and property defined in the implemented protocol.
CONS
- Supporting new product types. Since the Factory protocol defines all the various types of products that can be instantiated, adding a family means changing the factory interface. This change affects the concrete factories and all the subclasses, making the operation laborious.
If you have any question about this article, feel free to email me or tweet me @franceleonidev and share your opinion.
Thank you for reading and see you in the next article!
Share this article