Flyweight Pattern Concept
The Flyweight pattern addresses memory efficiency by sharing common data across multiple objects rather then storing duplicate information. This approach is particularly valuable when dealing with systems that require numerous fine-grained objects with significant overlapping attributes.
Consider an e-commerce shipping system where each package shares sender information but differs in recipient details. Instead of creating complete package objects with duplicated sender data, the Flyweight pattern separates intrinsic (shared) and extrinsic (unique) states, allowing efficient memory utilization.
Pattern Structure
Flyweight Interface: Defines the operations that accept extrinsic state
ConcreteFlyweight: Implements the flyweight interface and stores intrinsic state
FlyweightFactory: Creates and manages flyweight objects, ensuring proper sharing
Client: Maintains references to flyweights and computes or stores extrinsic state
Implementation Example: Package Shipping System
Flyweight Interface
public interface Shipment {
void processDelivery(Recipient recipient);
}
Concrete Flyweight Implementation
public class ProductShipment implements Shipment {
private final String productName;
private final String senderName;
private final String senderPhone;
private final String senderAddress;
public ProductShipment(String product, String sender, String phone, String address) {
this.productName = product;
this.senderName = sender;
this.senderPhone = phone;
this.senderAddress = address;
}
@Override
public void processDelivery(Recipient recipient) {
String message = "Shipping " + productName + " to " + recipient.getName() + "\n";
message += "Recipient Phone: " + recipient.getPhone() + "\n";
message += "Recipient Address: " + recipient.getAddress() + "\n";
message += "From: " + senderAddress;
System.out.println(message);
System.out.println(this);
System.out.println("--------------------------------");
}
}
Extrinsic State Class
public class Recipient {
private final String name;
private final String phone;
private final String address;
public Recipient(String name, String phone, String address) {
this.name = name;
this.phone = phone;
this.address = address;
}
public String getName() { return name; }
public String getPhone() { return phone; }
public String getAddress() { return address; }
}
Flyweight Factory
public class ShipmentFactory {
private static final ShipmentFactory instance = new ShipmentFactory();
private final Map<String, Shipment> shipments = new HashMap<>();
private ShipmentFactory() {}
public static ShipmentFactory getInstance() {
return instance;
}
public Shipment getShipment(String product, String sender, String phone, String address) {
String key = product + "|" + sender;
return shipments.computeIfAbsent(key,
k -> new ProductShipment(product, sender, phone, address));
}
}
Client Usage
public class ShippingClient {
public static void main(String[] args) {
ShipmentFactory factory = ShipmentFactory.getInstance();
Shipment shoeShipment = factory.getShipment("Nike Shoes", "Wang's Store",
"1502369877", "Nike Park, Jingzhou");
shoeShipment.processDelivery(new Recipient("Zhang Si", "1501236542", "Village A"));
Shipment pantsShipment = factory.getShipment("Nike Pants", "Wang's Store",
"1502369877", "Nike Park, Jingzhou");
pantsShipment.processDelivery(new Recipient("Wang Wu", "1602365895", "Village B"));
Shipment anotherShoeShipment = factory.getShipment("Nike Shoes", "Wang's Store",
"1502369877", "Nike Park, Jingzhou");
anotherShoeShipment.processDelivery(new Recipient("Li Liu", "17045683397", "Village C"));
}
}
Pattern Analysis
The Flyweight pattern distinguishes between intrinsic state (shared, immutable data) and extrinsic state (context-dependent data). Intrinsic state is stored within flyweight objects, while extrinsic state is passed to flyweight methods during operation.
Memory savings become significant when many objects share common data. The pattern is most effective when:
- Numerous objects can share substantial amounts of state
- Application doesn't depend on object iedntity
- Extrinsic state can be computed rather than stored
Potential drawbacks include increased system complexity and thread safety considerations when shared objects are accessed concurrently.
Composite Flyweights
Complex flyweight objects can be composed from multiple simple flyweights, creating hierarchical structures without storing redundant data. This approach further extends memory efficiency while maintaining object relationships.