Flyweight Design Pattern in Kotlin

1. Definition

The Flyweight Design Pattern is about using sharing to support large numbers of similar objects efficiently. It separates the intrinsic (shared) state from the extrinsic (unique) state of an object.

2. Problem Statement

Imagine you are developing a word-processing application. Each character might have its own font style (bold, italic, underline, font type, etc.). If every character is treated as a unique object with its style, a document can consume excessive memory.

3. Solution

Flyweight pattern suggests that you stop storing the redundant data within the objects. Instead, you extract the shared states (font styles) and place them into a shared container. The individual objects then reference these shared styles.

4. Real-World Use Cases

1. A text editor where different characters or words have different styles.

2. Browser rendering engines where CSS styles are applied to elements.

5. Implementation Steps

1. Identify the shared (intrinsic) and unique (extrinsic) states of the object.

2. Create a factory that caches and reuses instances of the object based on their intrinsic state.

3. The client code should use the factory instead of the "new" keyword.

6. Implementation in Kotlin

// Flyweight class
data class FontStyle(val fontName: String, val isBold: Boolean, val isItalic: Boolean)
// Factory class
object FontStyleFactory {
    private val styleMap = mutableMapOf<String, FontStyle>()
    fun getFontStyle(fontName: String, isBold: Boolean, isItalic: Boolean): FontStyle {
        val key = "$fontName-$isBold-$isItalic"
        return styleMap.getOrPut(key) {
            FontStyle(fontName, isBold, isItalic)
        }
    }
}
class Character(val char: Char, val style: FontStyle, val positionX: Int, val positionY: Int) {
    fun display() {
        println("Character '$char' with style $style at position ($positionX, $positionY)")
    }
}
fun main() {
    val style1 = FontStyleFactory.getFontStyle("Arial", true, false)
    val char1 = Character('A', style1, 10, 20)
    char1.display()
    val style2 = FontStyleFactory.getFontStyle("Arial", true, false)
    val char2 = Character('B', style2, 30, 40)
    char2.display()
    val style3 = FontStyleFactory.getFontStyle("Times New Roman", false, true)
    val char3 = Character('C', style3, 50, 60)
    char3.display()
}

Output:

Character 'A' with style FontStyle(fontName=Arial, isBold=true, isItalic=false) at position (10, 20)
Character 'B' with style FontStyle(fontName=Arial, isBold=true, isItalic=false) at position (30, 40)
Character 'C' with style FontStyle(fontName=Times New Roman, isBold=false, isItalic=true) at position (50, 60)

Explanation:

1. FontStyle is our Flyweight that represents the intrinsic shared state (font styles).

2. FontStyleFactory manages and caches FontStyle objects.

3. Character represents text characters with a given style and position.

4. In the main function, we use the factory to retrieve font styles. Though we request two Arial-Bold styles, they reference the same object, saving memory.

7. When to use?

Use the Flyweight pattern when:

1. You have a large number of objects, leading to high memory consumption.

2. Objects have shared states that can be externalized.

3. Objects can be replaced by shared objects once the extrinsic state is removed.


Comments