|
| 1 | +--- |
| 2 | +title: Flyweight Pattern |
| 3 | +tags: |
| 4 | + - structural |
| 5 | +created: 2026-04-16 |
| 6 | +--- |
| 7 | +## Definition |
| 8 | + |
| 9 | +The **Flyweight Pattern** is a structural design pattern used to reduce memory usage by sharing common object data among multiple objects. |
| 10 | + |
| 11 | +--- |
| 12 | +## Real World Analogy |
| 13 | + |
| 14 | +Imagine you are building a game like a forest simulator or something similar to Minecraft. In this game, you need to place thousands of trees across the map. All these trees might look exactly the same in terms of structure, color, and texture, but their positions will be different. |
| 15 | + |
| 16 | +If you create a separate object for every tree including its full structure, then for 10,000 trees you will end up creating 10,000 complete objects. This consumes a lot of memory because the same data is repeated again and again. |
| 17 | + |
| 18 | +Instead, the Flyweight Pattern helps you reuse the common part of the tree such as its type, color, and texture. This shared part is stored only once. Then each tree only stores its position like x and y. |
| 19 | + |
| 20 | +So instead of creating 10,000 full objects, you create: |
| 21 | +- 1 shared TreeType object |
| 22 | +- 10,000 Tree objects that only store position |
| 23 | + |
| 24 | +![[flyweight_explanation.png]] |
| 25 | + |
| 26 | +This significantly reduces memory usage. |
| 27 | + |
| 28 | +Now imagine scaling this to lakhs of trees. Without this pattern, memory usage would grow very quickly. With Flyweight, you avoid duplication and keep your application efficient. |
| 29 | + |
| 30 | +--- |
| 31 | +## Design |
| 32 | + |
| 33 | +```mermaid |
| 34 | +classDiagram |
| 35 | +
|
| 36 | +class TreeType { |
| 37 | + - name : String |
| 38 | + - color : String |
| 39 | + - texture : String |
| 40 | + + TreeType(name : String, color : String, texture : String) |
| 41 | + + display(x : int, y : int) |
| 42 | +} |
| 43 | +
|
| 44 | +class TreeTypeFactory { |
| 45 | + - treetypes : Map |
| 46 | + + getTreeType(name : String, color : String, texture : String) TreeType |
| 47 | +} |
| 48 | +
|
| 49 | +class Tree { |
| 50 | + - x : int |
| 51 | + - y : int |
| 52 | + - treeType : TreeType |
| 53 | + + Tree(x : int, y : int, treetype : TreeType) |
| 54 | + + display() |
| 55 | +} |
| 56 | +
|
| 57 | +class Client |
| 58 | +
|
| 59 | +Tree --> TreeType : uses |
| 60 | +TreeTypeFactory --> TreeType : creates |
| 61 | +Client --> TreeTypeFactory : calls |
| 62 | +Client --> Tree : creates |
| 63 | +``` |
| 64 | + |
| 65 | +This diagram shows how the shared object TreeType is reused across multiple Tree objects, while the factory ensures that duplicate objects are not created. |
| 66 | + |
| 67 | +--- |
| 68 | +## Implementation in Java |
| 69 | + |
| 70 | +```java title="TreeType.java" |
| 71 | +// Intrinsic State (Shared Object) |
| 72 | +class TreeType { |
| 73 | + private String name; |
| 74 | + private String color; |
| 75 | + private String texture; |
| 76 | + |
| 77 | + public TreeType(String name, String color, String texture) { |
| 78 | + this.color = color; |
| 79 | + this.name = name; |
| 80 | + this.texture = texture; |
| 81 | + } |
| 82 | + |
| 83 | + // Display the Tree based on different positions. |
| 84 | + public void display(int x, int y) { |
| 85 | + System.out.println("Tree: " + name + " at (" + x + "," + y + ")"); |
| 86 | + } |
| 87 | +} |
| 88 | +``` |
| 89 | +This class represents the shared part of the object. It stores intrinsic data like name, color, and texture which does not change. The display method takes position as input, which is external data. |
| 90 | + |
| 91 | +```java title="TreeTypeFactory.java" |
| 92 | +class TreeTypeFactory { |
| 93 | + // Map collection of each type of object |
| 94 | + private static Map<String, TreeType> treetypes = new HashMap<String, TreeType>(); |
| 95 | + |
| 96 | + // Get the Tree type according to the key from the map |
| 97 | + public static TreeType getTreeType(String name, String color, String texture) { |
| 98 | + String key = name + color + texture; |
| 99 | + if (!treetypes.containsKey(key)) { |
| 100 | + // Add the Tree type in the map to share the same object |
| 101 | + treetypes.put(key, new TreeType(name, color, texture)); |
| 102 | + } |
| 103 | + return treetypes.get(key); |
| 104 | + } |
| 105 | +} |
| 106 | +``` |
| 107 | +This factory class ensures that objects are reused. Before creating a new TreeType, it checks if one already exists in the map. If it exists, it returns the existing object. Otherwise, it creates and stores a new one. |
| 108 | + |
| 109 | +```java title="Tree.java" |
| 110 | +// Extrinsic State |
| 111 | +class Tree { |
| 112 | + private int x; |
| 113 | + private int y; |
| 114 | + private TreeType treeType; //shared object |
| 115 | + |
| 116 | + public Tree(int x, int y, TreeType treetype) { |
| 117 | + this.x = x; |
| 118 | + this.y = y; |
| 119 | + this.treeType = treetype; |
| 120 | + } |
| 121 | + |
| 122 | + // Display the Tree based on the shared object but on different positions |
| 123 | + public void display() { |
| 124 | + treeType.display(this.x, this.y); |
| 125 | + } |
| 126 | +} |
| 127 | +``` |
| 128 | +This class represents individual trees. It only stores the position which is the extrinsic state. The shared TreeType object is reused for all trees of the same type. |
| 129 | + |
| 130 | +```java title="FlyWeightPattern.java" |
| 131 | +public static void main(String[] args) { |
| 132 | + List<Tree> forest = new ArrayList<>(); |
| 133 | + |
| 134 | + for (int i = 0; i < 5; i++) { |
| 135 | + TreeType oak = TreeTypeFactory.getTreeType("Oak", "Green", "Rough"); |
| 136 | + forest.add(new Tree(i, i * 2, oak)); |
| 137 | + } |
| 138 | + |
| 139 | + for (Tree tree : forest) { |
| 140 | + tree.display(); |
| 141 | + } |
| 142 | +} |
| 143 | +``` |
| 144 | +Here we are creating multiple trees using the same TreeType object. The factory ensures that only one Oak type is created and reused. Each tree has a different position, but the structure remains shared. |
| 145 | + |
| 146 | +**Output**: |
| 147 | +```bash |
| 148 | +Tree: Oak at (0,0) |
| 149 | +Tree: Oak at (1,2) |
| 150 | +Tree: Oak at (2,4) |
| 151 | +Tree: Oak at (3,6) |
| 152 | +Tree: Oak at (4,8) |
| 153 | +``` |
| 154 | + |
| 155 | +--- |
| 156 | +## Real World Example |
| 157 | + |
| 158 | +- In Java, strings are a good example of the Flyweight Pattern. |
| 159 | + ```java |
| 160 | + String a="Final"; |
| 161 | + String b="Final"; |
| 162 | + System.out.println(a==b); |
| 163 | + |
| 164 | + System.out.println(a.hashCode() +" "+ b.hashCode()); |
| 165 | + ``` |
| 166 | + When you create two strings with the same value, Java does not create two separate objects. Instead, it stores them in a common memory area called the String Pool and reuses the same object. |
| 167 | + |
| 168 | + That is why both references point to the same memory and the hash codes are also the same. |
| 169 | + |
| 170 | +- Another example is in web development. You can define a CSS class once and reuse it across multiple buttons or elements. Instead of creating separate styles for each button, you reuse the same class, which reduces duplication and keeps things efficient. |
| 171 | +--- |
| 172 | +## Design Principles: |
| 173 | + |
| 174 | +- **Encapsulate What Varies** - Identify the parts of the code that are going to change and encapsulate them into separate class just like the Strategy Pattern. |
| 175 | +- **Favor Composition Over Inheritance** - Instead of using inheritance on extending functionality, rather use composition by delegating behavior to other objects. |
| 176 | +- **Program to Interface not Implementations** - Write code that depends on Abstractions or Interfaces rather than Concrete Classes. |
| 177 | +- **Strive for Loosely coupled design between objects that interact** - When implementing a class, avoid tightly coupled classes. Instead, use loosely coupled objects by leveraging abstractions and interfaces. This approach ensures that the class does not heavily depend on other classes. |
| 178 | +- **Classes Should be Open for Extension But closed for Modification** - Design your classes so you can extend their behavior without altering their existing, stable code. |
| 179 | +- **Depend on Abstractions, Do not depend on concrete class** - Rely on interfaces or abstract types instead of concrete classes so you can swap implementations without altering client code. |
| 180 | +- **Talk Only To Your Friends** - An object may only call methods on itself, its direct components, parameters passed in, or objects it creates. |
| 181 | +- **Don't call us, we'll call you** - This means the framework controls the flow of execution, not the user’s code (Inversion of Control). |
| 182 | +- **A class should have only one reason to change** - This emphasizes the Single Responsibility Principle, ensuring each class focuses on just one functionality. |
0 commit comments