In the previous article, we started to learn about Creational design patterns and discussed the Factory Method. This time, we will look at the Abstract Factory pattern.
An Abstract Factory can be helpful in situations where we want to hide the internal details of an object creation behind a public API. This pattern can also be beneficial when creating an object is a complex process that needs to be repeated many times throughout an application.
Unlike Java, C++, and C#, Swift doesn’t have an abstract class. But fear not — we can harness the power of Swift Protocols to implement a similar design pattern effectively.
The Abstract Factory design pattern has five main components:
- Abstract product
- Concrete product
- Abstract factory
- Concrete factory
- Client
Example of usage
Let’s bring the Abstract Factory pattern to life with a relatable example. Imagine you’re preparing a dinner. It could be a delicious fried meat, a refreshing salad, or a mouthwatering cake. Yes, we’re having cake for dinner in this scenario. This relatable example will help you understand how the Abstract Factory pattern can be practically applied.
As an Abstract product, we will use the MealProtocol.
protocol MealProtocol {
var name: String { get set }
func cook()
}
extension MealProtocol {
func cook() {
print("The \(name) is cooked")
}
}
We will use Salad, Cake, and Meat as Concrete products.
struct Meat: MealProtocol {
var name = "Beef"
}
struct Salad: MealProtocol {
var name = "Caesar Salad"
}
struct Cake: MealProtocol {
var name = "Chocolate Cake"
}
As an Abstract factory, we will use MealFactoryProtocol.
protocol MealFactoryProtocol {
func makeMeal() -> MealProtocol
}
We will use a Meat factory, a Salad Factory, and a Cake factory as Concrete factories.
final class MeatFactory: MealFactoryProtocol {
func makeMeal() -> MealProtocol {
Meat()
}
}
final class SaladFactory: MealFactoryProtocol {
func makeMeal() -> MealProtocol {
Salad()
}
}
final class CakeFactory: MealFactoryProtocol {
func makeMeal() -> MealProtocol {
Cake()
}
}
Our Client will take care of the rest and provide us with the dinner.
enum MealKind {
case salad, meat, cake
}
final class DinnerClient {
private let mealFactory: MealFactoryProtocol
init(mealKind: MealKind) {
self.mealFactory = switch mealKind {
case .salad:
SaladFactory()
case .meat:
MeatFactory()
case .cake:
CakeFactory()
}
}
func serve() -> MealProtocol {
let meal: MealProtocol = mealFactory.makeMeal()
meal.cook()
return meal
}
}
Conclusion
The job is done, and I hope you have enjoyed this article. The following article will discuss how and when to use the Builder Pattern. See you soon!
Check out other posts in the Design Patterns series:
- Visitor Design Pattern in Swift
- Themplate Method Design Pattern in Swift
- Strategy Design Pattern in Swift
- State Design Pattern in Swift
- Observer Design Pattern in Swift
- Memento Design Pattern in Swift
- Mediator Design Pattern in Swift
- Iterator Design Pattern in Swift
- Command Design Pattern in Swift
- Chain of Responsibility Design Pattern in Swift
- Proxy Design Pattern in Swift
- FlyWeight Design Pattern in Swift
- Facade Design Pattern in Swift
- Decorator Design Pattern in Swift
- Composite Design Pattern in Swift
- Bridge Design Pattern in Swift
- Adapter Design Pattern in Swift
- Singleton Design Pattern in Swift
- Prototype Design Pattern in Swift
- Builder Design Pattern in Swift
- Factory Method Design Pattern in Swift
- Design Patterns: Basics