Greetings, traveler!
We continue to explore Structural Design Patterns. In this article, we will discuss the FlyWeight Pattern. Although this pattern may initially seem challenging, its application can be pretty specific.
So, what exactly is the FlyWeight pattern? It’s a structural pattern that optimizes memory usage by allowing more objects to fit within the available RAM. This efficiency is achieved by sharing a common state among objects rather than duplicating the same data multiple times.
The FlyWeight pattern suggests avoiding storing external state within a class and instead passing it as parameters to methods. This allows the same object to be reused in different contexts. But if you require multiple instances of an internal state object, consider using the Singleton or Factory patterns.
Example of usage
Let’s take the example of a car dealership as an illustration. Let’s say we only sell one car brand, but that brand has three different body styles: sedan, hatchback, and coupe.
enum CardKind: String {
case sedan, hatchback, coupe
}
The car will act as a flyweight, with one property indicating its kind.
struct CarFlyWeight {
let kind: CardKind
}
We must create a warehouse for these cars to act as a factory and cache data.
protocol CarFactoryProtocol {
func makeCar(_ kind: CardKind) -> CarFlyWeight
}
final class WareHouse: CarFactoryProtocol {
private var cache: [CardKind: CarFlyWeight] = [:]
func makeCar(_ kind: CardKind) -> CarFlyWeight {
guard let car = cache[kind] else {
let car = CarFlyWeight(kind: kind)
cache[kind] = car
return car
}
return car
}
}
Now, we will create our dealership. We have access to the warehouse and a dictionary containing the necessary information. We need to know the deal’s ID and the kind of car we are selling. With the dealership’s help, we can create new deals and view a report on all existing ones.
final class CarDealership {
private let factory: CarFactoryProtocol
private var deals: [Int: CarFlyWeight] = [:]
init(factory: CarFactoryProtocol) {
self.factory = factory
}
func makeDeal(id: Int, car: CardKind) {
deals[id] = factory.makeCar(car)
}
func report() {
deals.forEach { id, car in
print("Deal id: \(id); Car: \(car)")
}
}
}
And now we can use our code.
let dealership = CarDealership(factory: WareHouse())
dealership.makeDeal(id: 1, car: .coupe)
dealership.report()
Conclusion
The Flyweight pattern is a useful design pattern that helps improve performance in situations where many objects share similar data. You can use this pattern to simplify your code, but it can become more complex, which should be avoided.
This concludes the discussion of this pattern. The following article will discuss another structural design pattern, the Proxy.
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
- 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
- Abstract Factory Design Pattern in Swift
- Factory Method Design Pattern in Swift
- Design Patterns: Basics