Skip to main content

Flyweight

StructuralGang of FourPerformanceAbout 2 min

Intent

Use sharing to support large numbers of fine-grained objects efficiently.

Explanation

Real-world example

Alchemist's shop has shelves full of magic potions. Many of the potions are the same so there is
no need to create a new object for each of them. Instead, one object instance can represent
multiple shelf items so the memory footprint remains small.

In plain words

It is used to minimize memory usage or computational expenses by sharing as much as possible with
similar objects.

Wikipedia says

In computer programming, flyweight is a software design pattern. A flyweight is an object that
minimizes memory use by sharing as much data as possible with other similar objects; it is a way
to use objects in large numbers when a simple repeated representation would use an unacceptable
amount of memory.

Programmatic example

Translating our alchemist shop example from above. First of all, we have different potion types:

public interface Potion {
  void drink();
}

@Slf4j
public class HealingPotion implements Potion {
  @Override
  public void drink() {
    LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this));
  }
}

@Slf4j
public class HolyWaterPotion implements Potion {
  @Override
  public void drink() {
    LOGGER.info("You feel blessed. (Potion={})", System.identityHashCode(this));
  }
}

@Slf4j
public class InvisibilityPotion implements Potion {
  @Override
  public void drink() {
    LOGGER.info("You become invisible. (Potion={})", System.identityHashCode(this));
  }
}

Then the actual Flyweight class PotionFactory, which is the factory for creating potions.

public class PotionFactory {

  private final Map<PotionType, Potion> potions;

  public PotionFactory() {
    potions = new EnumMap<>(PotionType.class);
  }

  Potion createPotion(PotionType type) {
    var potion = potions.get(type);
    if (potion == null) {
      switch (type) {
        case HEALING -> {
          potion = new HealingPotion();
          potions.put(type, potion);
        }
        case HOLY_WATER -> {
          potion = new HolyWaterPotion();
          potions.put(type, potion);
        }
        case INVISIBILITY -> {
          potion = new InvisibilityPotion();
          potions.put(type, potion);
        }
        default -> {
        }
      }
    }
    return potion;
  }
}

AlchemistShop contains two shelves of magic potions. The potions are created using the
aforementioned PotionFactory.

@Slf4j
public class AlchemistShop {

  private final List<Potion> topShelf;
  private final List<Potion> bottomShelf;

  public AlchemistShop() {
    var factory = new PotionFactory();
    topShelf = List.of(
        factory.createPotion(PotionType.INVISIBILITY),
        factory.createPotion(PotionType.INVISIBILITY),
        factory.createPotion(PotionType.STRENGTH),
        factory.createPotion(PotionType.HEALING),
        factory.createPotion(PotionType.INVISIBILITY),
        factory.createPotion(PotionType.STRENGTH),
        factory.createPotion(PotionType.HEALING),
        factory.createPotion(PotionType.HEALING)
    );
    bottomShelf = List.of(
        factory.createPotion(PotionType.POISON),
        factory.createPotion(PotionType.POISON),
        factory.createPotion(PotionType.POISON),
        factory.createPotion(PotionType.HOLY_WATER),
        factory.createPotion(PotionType.HOLY_WATER)
    );
  }

  public final List<Potion> getTopShelf() {
    return List.copyOf(this.topShelf);
  }

  public final List<Potion> getBottomShelf() {
    return List.copyOf(this.bottomShelf);
  }

  public void drinkPotions() {
    LOGGER.info("Drinking top shelf potions\n");
    topShelf.forEach(Potion::drink);
    LOGGER.info("Drinking bottom shelf potions\n");
    bottomShelf.forEach(Potion::drink);
  }
}

In our scenario, a brave visitor enters the alchemist shop and drinks all the potions.

// create the alchemist shop with the potions
var alchemistShop = new AlchemistShop();
// a brave visitor enters the alchemist shop and drinks all the potions
alchemistShop.drinkPotions();

Program output:

Drinking top shelf potions 
You become invisible. (Potion=1509514333)
You become invisible. (Potion=1509514333)
You feel strong. (Potion=739498517)
You feel healed. (Potion=125130493)
You become invisible. (Potion=1509514333)
You feel strong. (Potion=739498517)
You feel healed. (Potion=125130493)
You feel healed. (Potion=125130493)
Drinking bottom shelf potions
Urgh! This is poisonous. (Potion=166239592)
Urgh! This is poisonous. (Potion=166239592)
Urgh! This is poisonous. (Potion=166239592)
You feel blessed. (Potion=991505714)
You feel blessed. (Potion=991505714)

Class diagram

alt text
Flyweight pattern class diagram

Applicability

The Flyweight pattern's effectiveness depends heavily on how and where it's used. Apply the
Flyweight pattern when all of the following are true:

  • An application uses a large number of objects.
  • Storage costs are high because of the sheer quantity of objects.
  • Most of the object state can be made extrinsic.
  • Many groups of objects may be replaced by relatively few shared objects once the extrinsic state
    is removed.
  • The application doesn't depend on object identity. Since flyweight objects may be shared, identity
    tests will return true for conceptually distinct objects.

Known uses

Credits