Link Search Menu Expand Document

The Flyweight Design Pattern

The Flyweight Pattern is designed to share objects (to reuse instances) for better efficiency and consistency.


What problems does it solve?

It solves memory issues and lowers the needed memory and can be used for caching. You can optimize the memory and lower RAM usage by dividing the objects into two parts:

  • shareable (intrinsic) state
  • non-shareable (extrinsic) state. Then objects with common data can use the same shared state, instead of replicating the common data for each object.

Glossary:

  • Flyweight - holds/ represents the shareable (intrinsic/ immutable) state of an object
  • FlyweightFactory - holds all Flyweight objects (like cache) and “creates” new Flyweight object (if not available in cache or returns the available)
  • Concrete object - holds/ represents the non-shareable (extrinsic/ mutable) state. This is the state that changes over time or is unique for the object
  • Client - the one, who uses the objects with their shareable and non-shareable states

Pros:

  • Lowers the memory usage of your application by decreasing duplicated data (even object instances)

Cons:

  • Increases the complexity of the code
  • There is an overhead if you need to recalculate/recreate/sync the shareable (intrinsic) state of the object

How to recognize it?

When you call creational method, that returns a cached instance instead of a new one.

public final class CountryFactory {

    private static Map<String, Country> cache = new HashMap<>();

    private CountryFactory() {
        // hidden
    }

    public static Country createCountry(String name) {
        if (!cache.containsKey(name)) {
            cache.put(name, new Country(name));
        }
        return cache.get(name);
    }
}

Examples from Java API

java.lang.Integer#valueOf(int) (also on Boolean, Byte, Character, Short, Long and BigDecimal)

Scenarios

  • When you have common data that is shared between multiple objects
  • When the application creates large number of similar objects (too many instances)
  • When you need to reduce the storage cost of the application
  • When you need to implement a caching mechanism

Example 1

Let’s say we have a register of landmarks. We want to browse landmarks by country. As there are many landmarks in a single country, we can say that multiple landmarks “share the same country”. This means that the class Country can be a Flyweight object and class “Landmark” is our unique object, that shares common data (same country) with other unique objects.

Source Code

1). Create class Country

public class Country {

    private String name;

    public Country(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return super.toString().concat("(" + this.name + ")");
    }
}

2). Create Factory, which will create new country if necessary or return an existing instance

public final class CountryFactory {

    private static Map<String, Country> cache = new HashMap<>();

    private CountryFactory() {
        // hidden
    }

    public static Country createCountry(String name) {
        if (!cache.containsKey(name)) {
            cache.put(name, new Country(name));
        }
        return cache.get(name);
    }
}

3). Create a class Landmark (The unique data)

@ToString
public class Landmark {

    @Getter
    private Country country;

    private String landmarkName;

    private String landmarkDescription;

    public Landmark(String countryName, String landmarkName, String landmarkDescription) {
        this.country = CountryFactory.createCountry(countryName);
        this.landmarkName = landmarkName;
        this.landmarkDescription = landmarkDescription;
    }
}

4). Create a demo class

public class _Main {

    public static void main(String[] args) {

        List<Landmark> landmarks = new ArrayList<>();

        landmarks.add(new Landmark("Italy", "St Peter's Basilica, Vatican City", "It remains one of the two largest churches in the world."));
        landmarks.add(new Landmark("Italy", "Milan Cathedral (Duomo) – Milan, Italy", "It is the largest Gothic cathedral and the second largest Catholic cathedral in the world."));
        landmarks.add(new Landmark("Cambodia", "Siem Reap", "The majestic structure is Cambodia's most beloved and best-preserved temple."));
        landmarks.add(new Landmark("Peru", "Machu Picchu", "Located 8,000 ft high in the Andes, Peru's famous lost city is one of the most famous and spectacular ruins in the world."));
        landmarks.add(new Landmark("India", "Taj Mahal – Angra", "Standing majestically on the banks of the River Yamuna, India's national treasure is a symbol of love and romance. "));

        for (Landmark landmark : landmarks) {
            System.out.println(landmark);
        }
    }
}

Output:

Landmark(country=Country@e580929(Italy), landmarkName=St Peter's Basilica, Vatican City, landmarkDescription=It remains one of the two largest churches in the world.)
Landmark(country=Country@e580929(Italy), landmarkName=Milan Cathedral (Duomo) – Milan, Italy, landmarkDescription=It is the largest Gothic cathedral and the second largest Catholic cathedral in the world.)
Landmark(country=Country@1cd072a9(Cambodia), landmarkName=Siem Reap, landmarkDescription=The majestic structure is Cambodia's most beloved and best-preserved temple.)
Landmark(country=Country@7c75222b(Peru), landmarkName=Machu Picchu, landmarkDescription=Located 8,000 ft high in the Andes, Peru's famous lost city is one of the most famous and spectacular ruins in the world.)
Landmark(country=Country@4c203ea1(India), landmarkName=Taj Mahal – Angra, landmarkDescription=Standing majestically on the banks of the River Yamuna, India's national treasure is a symbol of love and romance. )

As you can see, landmarks in Italy share the same country instance “Country@e580929(Italy)”