Flyweight Design Pattern in Kotlin

1. Definition

The Flyweight Design Pattern aims to minimize memory usage and computational costs by sharing as much as possible with similar objects. It's all about using shared objects that can be used in multiple contexts instead of creating new ones.

2. Problem Statement

Imagine having to manage a large number of objects with some shared data. Creating an individual object for each can consume significant memory and degrade performance.

3. Solution

The Flyweight pattern suggests creating a common interface for flyweight objects and then segregating the intrinsic (shared) and extrinsic (unique) states. Shared data is stored in flyweight objects, while unique data is stored or computed externally and passed to the flyweights when needed.

4. Real-World Use Cases

1. Text editors, where each character's glyph is reused, but its position and style might vary.

2. Game engines that use graphics for terrain, trees, or other common elements.

5. Implementation Steps

1. Identify the common states (intrinsic) and the unique states (extrinsic).

2. Create a flyweight interface and concrete flyweights to encapsulate the shared states.

3. The client code should differentiate between intrinsic and extrinsic states.

6. Implementation in Kotlin Programming

// Step 1: Flyweight Interface
interface Shape {
    fun draw(x: Int, y: Int, color: String)
}
// Step 2: Concrete Flyweight
class Circle(private val radius: Int) : Shape {
    override fun draw(x: Int, y: Int, color: String) {
        println("Drawing Circle at ($x,$y) with radius $radius and color $color")
    }
}
// Step 3: Flyweight Factory
object ShapeFactory {
    private val circleMap = mutableMapOf<Int, Circle>()
    fun getCircle(radius: Int): Circle {
        if (!circleMap.containsKey(radius)) {
            circleMap[radius] = Circle(radius)
        }
        return circleMap[radius]!!
    }
}
// Client Code
fun main() {
    val circle1 = ShapeFactory.getCircle(5)
    circle1.draw(10, 10, "Red")
    val circle2 = ShapeFactory.getCircle(5)
    circle2.draw(20, 20, "Blue")
    val circle3 = ShapeFactory.getCircle(10)
    circle3.draw(30, 30, "Green")
}

Output:

Drawing Circle at (10,10) with radius 5 and color Red
Drawing Circle at (20,20) with radius 5 and color Blue
Drawing Circle at (30,30) with radius 10 and color Green

Explanation:

1. Shape is a flyweight interface with a draw method.

2. Circle is a concrete flyweight storing the intrinsic state (radius) and taking the extrinsic state (position and color) as parameters in the draw method.

3. ShapeFactory acts as a factory to create and manage flyweight objects. If a circle with a specific radius already exists, it is returned; otherwise, a new one is created.

4. In the client code, circles are drawn at different positions and colors but share the same radius.

7. When to use?

Use the Flyweight pattern when:

1. A large number of similar objects use more memory than necessary because of duplicated data.

2. The object's uniqueness can be split into shared and unique components.

3. You want to avoid spending resources on bulk memory allocations.

Comments