3.6. Flyweight

Maheshmaddi
4 min readApr 9, 2023

--

The Flyweight pattern is a structural design pattern that aims to minimize memory usage and improve performance by sharing common parts of objects between multiple objects. It is especially useful when dealing with a large number of objects that have some shared state.

The Flyweight pattern is typically used when:

  1. You have a large number of objects, and the storage costs are high.
  2. Most of the object’s state can be made extrinsic (external) and shared.
  3. The application doesn’t rely on object identity, and objects can be shared without compromising the logic.

To implement the Flyweight pattern, follow these steps:

  1. Identify the common state that can be shared between multiple objects (intrinsic state) and the unique state that cannot be shared (extrinsic state).
  2. Create a flyweight interface or abstract class that specifies the common methods for both shared and unique states.
  3. Create a concrete flyweight class that implements the flyweight interface or extends the abstract class and stores the shared state.
  4. Create a flyweight factory that manages the creation and sharing of flyweight objects, ensuring that existing shared objects are reused and new objects are created only when necessary.
  5. Adjust the client code to use the flyweight factory for creating and managing flyweight objects, and to pass the extrinsic state to the flyweight methods when needed.

Here’s a simple example of the Flyweight pattern in Java:

import java.util.HashMap;
import java.util.Map;

// Flyweight interface
public interface Flyweight {
void operation(String extrinsicState);
}

// Concrete flyweight
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;

public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}

@Override
public void operation(String extrinsicState) {
System.out.println("Intrinsic state: " + intrinsicState + ", Extrinsic state: " + extrinsicState);
}
}

// Flyweight factory
public class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();

public Flyweight getFlyweight(String intrinsicState) {
Flyweight flyweight = flyweights.get(intrinsicState);
if (flyweight == null) {
flyweight = new ConcreteFlyweight(intrinsicState);
flyweights.put(intrinsicState, flyweight);
}
return flyweight;
}
}

// Client code
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("A");
Flyweight flyweight2 = factory.getFlyweight("A");

flyweight1.operation("1"); // Outputs: "Intrinsic state: A, Extrinsic state: 1"
flyweight2.operation("2"); // Outputs: "Intrinsic state: A, Extrinsic state: 2"
}
}

In this example, the Flyweight interface represents the common interface for flyweight objects, while the ConcreteFlyweight class implements the Flyweight interface and stores the shared intrinsic state. The FlyweightFactory class manages the creation and sharing of flyweight objects.

Advantages of the Flyweight pattern:

  1. Reduced memory usage: The Flyweight pattern helps reduce memory usage by sharing common parts of objects between multiple objects.
  2. Improved performance: By reusing shared objects, the pattern can lead to improved performance, especially in cases with a large number of objects.

Disadvantages of the Flyweight pattern:

  1. Increased complexity: The Flyweight pattern introduces additional classes and logic, which can increase the overall complexity of the code.
  2. Implementation challenges: Identifying the intrinsic and extrinsic states and separating them can be challenging, and might require significant refactoring of the existing code.

When using the Flyweight pattern, consider its benefits and drawbacks carefully. Use the pattern when you need to minimize memory usage and improve performance in scenarios with a large number of objects sharing some common state. Be aware of the potential complexity introduced by the pattern and the challenges associated with identifying and separating intrinsic and extrinsic states. As a best practice, apply the pattern judiciously to maintain a clean and understandable codebase.

Use case: Flyweight Pattern for Text Formatting

Class diagram for Flyweight Pattern for Text Formatting

Imagine we’re building a text editor that can apply various formatting options to the text (e.g., bold, italic, underline, font size, etc.). Each character in the text can have a different style, but there will be many characters with the same style. To optimize memory usage and prevent the creation of a large number of similar objects, we can use the Flyweight pattern.

Java Code:

  1. Flyweight interface:
public interface TextFormatFlyweight {
void applyFormat(Character character);
}

2. Concrete Flyweight implementation:

public class TextFormat implements TextFormatFlyweight {
private boolean bold;
private boolean italic;
private boolean underline;
private int fontSize;

public TextFormat(boolean bold, boolean italic, boolean underline, int fontSize) {
this.bold = bold;
this.italic = italic;
this.underline = underline;
this.fontSize = fontSize;
}

@Override
public void applyFormat(Character character) {
System.out.print(character);
if (bold) System.out.print(" [Bold]");
if (italic) System.out.print(" [Italic]");
if (underline) System.out.print(" [Underline]");
System.out.println(" [Font size: " + fontSize + "]");
}
}

3. Flyweight Factory:

import java.util.HashMap;
import java.util.Map;

public class TextFormatFactory {
private Map<String, TextFormat> formats = new HashMap<>();

public TextFormat getFormat(boolean bold, boolean italic, boolean underline, int fontSize) {
String key = String.format("%b%b%b%d", bold, italic, underline, fontSize);
TextFormat format = formats.get(key);
if (format == null) {
format = new TextFormat(bold, italic, underline, fontSize);
formats.put(key, format);
}
return format;
}
}

4. Client code:

public class FlyweightDemo {
public static void main(String[] args) {
String text = "Flyweight pattern example.";
TextFormatFactory formatFactory = new TextFormatFactory();

for (char character : text.toCharArray()) {
TextFormat format;
if (Character.isUpperCase(character)) {
format = formatFactory.getFormat(true, false, false, 12);
} else {
format = formatFactory.getFormat(false, false, false, 12);
}
format.applyFormat(character);
}
}
}

In this example, we have a TextFormatFlyweight interface and its implementation TextFormat, which stores the formatting information. The TextFormatFactory class is responsible for managing the instances of TextFormat. The client code demonstrates how to use the Flyweight pattern to apply different formatting options to characters in a text string.

Note: For complete list of design patterns click here

--

--