The Composite pattern and the Flyweight pattern are both structural design patterns that are used to deal with objects in large quantities. However, they address different aspects of the object structure, and their implementations vary accordingly.
The Composite pattern is used when we need to treat a group of objects in the same manner as a single object. The pattern allows us to compose objects into a tree structure and to work with individual objects and groups of objects uniformly. The key benefit of the Composite pattern is that it enables us to create hierarchical structures with simple components, making them easy to add, remove, or modify. The pattern defines two types of objects, namely the **composite** objects and the **leaf** objects. The composite objects are containers, that is, they contain one or more components (either composite or leaf), whereas the leaf objects are the smallest unit of the composition.
Here is an example of the Composite pattern implemented in Java:
public interface Component {
void operation();
}
public class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf operation executed");
}
}
public class Composite implements Component {
private List<Component> components = new ArrayList<>();
public void addComponent(Component component) {
components.add(component);
}
public void removeComponent(Component component) {
components.remove(component);
}
@Override
public void operation() {
System.out.println("Composite operation executed");
for (Component component : components) {
component.operation();
}
}
}
The Flyweight pattern, on the other hand, is used to minimize memory usage when dealing with large quantities of objects. The pattern is based on the idea of sharing objects that have similar characteristics, instead of creating new objects each time. This means that the pattern tries to maximize the number of objects that can be shared (i.e., made into "flyweights"), while minimizing the number of objects that are unique. The pattern defines two types of objects, namely the **flyweight** objects and the **context** objects. The flyweight objects are the objects that can be shared among multiple context objects, whereas the context objects are the objects that contain the unique state of the objects.
Here is an example of the Flyweight pattern implemented in Java:
public interface Flyweight {
void operation(Context context);
}
public class ConcreteFlyweight implements Flyweight {
private final String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(Context context) {
System.out.println(String.format("ConcreteFlyweight %s: %s", intrinsicState, context.getState()));
}
}
public class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String intrinsicState) {
if (!flyweights.containsKey(intrinsicState)) {
flyweights.put(intrinsicState, new ConcreteFlyweight(intrinsicState));
}
return flyweights.get(intrinsicState);
}
}
public class Context {
private final String state;
private final Flyweight flyweight;
public Context(String state, Flyweight flyweight) {
this.state = state;
this.flyweight = flyweight;
}
public String getState() {
return state;
}
public void operation() {
flyweight.operation(this);
}
}
In summary, the Composite pattern is used to treat a group of objects in the same manner as an individual object, whereas the Flyweight pattern is used to minimize memory usage when dealing with large quantities of objects. The Composite pattern allows the creation of hierarchical structures of simple components, and the Flyweight pattern enables the sharing of objects that have similar characteristics to minimize the number of unique objects.