Open In App

Flyweight Method Design Pattern in Java

A flyweight design pattern or flyweight method is defined as a structural pattern that is used to minimize memory usage or computational expenses by sharing as much as possible with other similar objects.

The key idea behind the Flyweight pattern is to use shared objects to support large numbers of fine-grained objects efficiently.



Key Component of Flyweight Design Patterns:

Flyweight Interface/Abstract Class: Define an interface or an abstract class that declares the methods that must be implemented by concrete flyweight objects. This interface usually represents the common functionality shared among the flyweight objects.






public interface Flyweight {
    void operation();
}

Concrete Flyweight: Implement the Flyweight interface to create concrete flyweight objects. These objects are shared and can be used in multiple contexts.




public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;
 
    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }
 
    @Override
    public void operation() {
        System.out.println("ConcreteFlyweight: " + intrinsicState);
    }
}

Flyweight Factory: Create a factory class that manages the creation and sharing of flyweight objects. The factory maintains a pool or cache of existing flyweight objects and returns them when requested.




import java.util.HashMap;
import java.util.Map;
 
public class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();
 
    public Flyweight getFlyweight(String key) {
        if (!flyweights.containsKey(key)) {
            flyweights.put(key, new ConcreteFlyweight(key));
        }
        return flyweights.get(key);
    }
}

Client: The client is responsible for using the flyweight objects. It typically maintains the extrinsic state, which is unique to each instance.




public class Client {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
 
        Flyweight flyweight1 = factory.getFlyweight("shared");
        Flyweight flyweight2 = factory.getFlyweight("shared");
 
        flyweight1.operation();  // Output: ConcreteFlyweight: shared
        flyweight2.operation();  // Output: ConcreteFlyweight: shared
    }
}

In this pattern, the intrinsic state is shared among multiple instances, while the extrinsic state is passed as a parameter to the methods of the flyweight objects. This allows the flyweight objects to be reused in different contexts, reducing memory usage and improving performance.

Example of flyweight Design Patterns:

Problem Statement:

Here, we’ll create a ‘CoffeeOrder’ system where each order has intrinsic state (type of coffee) and extrinsic state (table number). so we will use Flyweight method pattern here to solve this case.

Overall Code for the above example:




import java.util.HashMap;
import java.util.Map;
 
// Flyweight interface
interface CoffeeOrder {
    void serveCoffee(CoffeeOrderContext context);
}
 
// ConcreteFlyweight
class CoffeeFlavor implements CoffeeOrder {
    private final String flavor;
 
    public CoffeeFlavor(String flavor) {
        this.flavor = flavor;
    }
 
    @Override
    public void serveCoffee(CoffeeOrderContext context) {
        System.out.println("Serving coffee flavor " + flavor + " to table " + context.getTableNumber());
    }
}
 
// Context class to hold extrinsic state
class CoffeeOrderContext {
    private final int tableNumber;
 
    public CoffeeOrderContext(int tableNumber) {
        this.tableNumber = tableNumber;
    }
 
    public int getTableNumber() {
        return tableNumber;
    }
}
 
// Flyweight factory
class CoffeeOrderFactory {
    private final Map<String, CoffeeOrder> flavors = new HashMap<>();
 
    public CoffeeOrder getCoffeeOrder(String flavor) {
        CoffeeOrder coffeeOrder = flavors.get(flavor);
 
        if (coffeeOrder == null) {
            coffeeOrder = new CoffeeFlavor(flavor);
            flavors.put(flavor, coffeeOrder);
        }
 
        return coffeeOrder;
    }
}
 
// Client code
public class CoffeeShop {
    public static void main(String[] args) {
        CoffeeOrderFactory coffeeOrderFactory = new CoffeeOrderFactory();
 
        // Orders with intrinsic state (coffee flavor) and extrinsic state (table number)
        CoffeeOrder order1 = coffeeOrderFactory.getCoffeeOrder("Cappuccino");
        order1.serveCoffee(new CoffeeOrderContext(1));
 
        CoffeeOrder order2 = coffeeOrderFactory.getCoffeeOrder("Espresso");
        order2.serveCoffee(new CoffeeOrderContext(2));
 
        CoffeeOrder order3 = coffeeOrderFactory.getCoffeeOrder("Cappuccino");
        order3.serveCoffee(new CoffeeOrderContext(3));
 
        // The flavor "Cappuccino" is shared between order1 and order3
        // It reduces memory usage as the intrinsic state is shared among orders with the same flavor
    }
}

Output
Serving coffee flavor Cappuccino to table 1
Serving coffee flavor Espresso to table 2
Serving coffee flavor Cappuccino to table 3




Explanation of the above example:

In this example,

Diagrammatic Representation of Flyweight Design Pattern in Java

Explanation

In the above figure:

Use Case of Flyweight Design Patterns in Java

Here are some common use cases for the Flyweight pattern:

By applying the Flyweight pattern in these scenarios, developers can achieve a balance between memory efficiency and performance, especially in situations where a large number of similar objects need to be managed.

Advantages of Flyweight Design Patterns in Java

Here are some advantages of using the Flyweight pattern:

In summary, the Flyweight pattern is beneficial in situations where there is a need for a large number of similar objects, and memory or computational efficiency is a concern. It promotes reusability, reduces redundancy, and can lead to improved system performance.

Disadvantages of Flyweight Design Patterns in Java

While the Flyweight pattern offers several advantages, it also comes with certain disadvantages and considerations. Here are some potential drawbacks:

It’s important to carefully consider the specific requirements and characteristics of a given problem before deciding to apply the Flyweight pattern. In some cases, the benefits may outweigh the drawbacks, while in others, the pattern may introduce unnecessary complexity.


Article Tags :