Flyweight Design Pattern in Swift

1. Definition

The Flyweight design pattern is a structural pattern that allows programs to support vast quantities of objects efficiently by sharing common parts of object state among multiple objects, thereby reducing the memory footprint of the objects.

2. Problem Statement

Suppose you're building a graphic editor that allows users to draw thousands of shapes. Each shape might have common data like color, gradient, and texture. If each object stores this data separately, it will consume a lot of memory, especially when the number of objects increases.

3. Solution

With the Flyweight pattern, instead of storing repetitive data in each object, you can extract the common data into a separate object called 'flyweight' and reference it from the original objects. This way, you only store common data once, reducing the overall memory usage.

4. Real-World Use Cases

1. Text editors that handle multiple fonts and characters but share the same character glyphs for rendering.

2. Games where multiple characters might have similar attributes like strength, agility, or appearance but differ in their behavior or position.

5. Implementation Steps

1. Identify the common data (intrinsic state) that can be shared across the different objects.

2. Create a flyweight class that will store this common data.

3. Ensure that the client code references the flyweight object instead of storing replicated data.

6. Implementation in Swift Programming

// Flyweight Class
class ShapeFlyweight {
    var color: String
    init(color: String) {
        self.color = color
    }
}
// Original Class
class Circle {
    private var radius: Int
    private var position: (x: Int, y: Int)
    private var flyweight: ShapeFlyweight
    init(radius: Int, position: (x: Int, y: Int), color: String, flyweightFactory: FlyweightFactory) {
        self.radius = radius
        self.position = position
        self.flyweight = flyweightFactory.getFlyweight(color: color)
    }
    func draw() {
        print("Drawing a \(flyweight.color) circle of radius \(radius) at \(position)")
    }
}
// Flyweight Factory
class FlyweightFactory {
    private var flyweights: [String: ShapeFlyweight] = [:]
    func getFlyweight(color: String) -> ShapeFlyweight {
        if let existingFlyweight = flyweights[color] {
            return existingFlyweight
        } else {
            let newFlyweight = ShapeFlyweight(color: color)
            flyweights[color] = newFlyweight
            return newFlyweight
        }
    }
}
// Client Code
let flyweightFactory = FlyweightFactory()
let circle1 = Circle(radius: 5, position: (x: 10, y: 10), color: "red", flyweightFactory: flyweightFactory)
let circle2 = Circle(radius: 8, position: (x: 20, y: 20), color: "red", flyweightFactory: flyweightFactory)
circle1.draw()
circle2.draw()

Output:

Drawing a red circle of radius 5 at (x: 10, y: 10)
Drawing a red circle of radius 8 at (x: 20, y: 20)

Explanation:

1. We identified that the 'color' attribute can be shared across multiple Circle objects.

2. The ShapeFlyweight class is created to store this shared attribute.

3. The FlyweightFactory helps in reusing existing flyweight objects or creating a new one if it doesn't exist.

4. In the client code, we create two circles with the same color. Thanks to the flyweight pattern, the color "red" is stored just once in memory.

7. When to use?

Use the Flyweight pattern when:

1. An application needs a large number of objects which share common parts of state, but not all of them can be shared.

2. Due to the sheer number of objects, storage costs are high because of repetitive information.

3. The majority of each object's state data can be made extrinsic.


Comments