享元模式(Flyweight)


目的
使用共享可以有效地支持大量细粒度对象。
说明
炼金术士的商店里摆满了魔法药水。许多药水是相同的,因此不需要为每个药水创建新的对象。相反,一个对象实例可以表示多个货架项目,因此内存占用空间很小
简而言之
它用于通过尽可能多地与类似对象共享来使内存使用最小化。
源码示例
从上面翻译我们的炼金术商店示例。首先,我们有不同的药水类型

public interface Potion {
  void drink();
}

public class HealingPotion implements Potion {
  private static final Logger LOGGER = LoggerFactory.getLogger(HealingPotion.class);
  @Override
  public void drink() {
    LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this));
  }
}

public class HolyWaterPotion implements Potion {
  private static final Logger LOGGER = LoggerFactory.getLogger(HolyWaterPotion.class);
  @Override
  public void drink() {
    LOGGER.info(
"You feel blessed. (Potion={})", System.identityHashCode(this));
  }
}

public class InvisibilityPotion implements Potion {
  private static final Logger LOGGER = LoggerFactory.getLogger(InvisibilityPotion.class);
  @Override
  public void drink() {
    LOGGER.info(
"You become invisible. (Potion={})", System.identityHashCode(this));
  }
}

然后是实际的Flyweight对象,它是用于创建药水的工厂

public class PotionFactory {

  private final Map<PotionType, Potion> potions;

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

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

它的用途如下

PotionFactory factory = new PotionFactory();
factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818)
factory.createPotion(PotionType.HEALING).drink();
// You feel healed. (Potion=648129364)
factory.createPotion(PotionType.INVISIBILITY).drink();
// You become invisible. (Potion=6566818)
factory.createPotion(PotionType.HOLY_WATER).drink();
// You feel blessed. (Potion=1104106489)
factory.createPotion(PotionType.HOLY_WATER).drink();
// You feel blessed. (Potion=1104106489)
factory.createPotion(PotionType.HEALING).drink();
// You feel healed. (Potion=648129364)

适用场景
Flyweight模式的有效性在很大程度上取决于它的使用方式和位置。满足以下所有条件时应用Flyweight模式

  • 应用程序使用大量对象
  • 由于对象数量众多,存储成本很高
  • 大多数对象状态可以是外部的
  • 一旦外部状态被移除,许多对象组可被相对较少的共享对象替换
  • 应用程序不依赖于对象标识。由于可以共享flyweight对象,因此对于概念上不同的对象,身份测试将返回true。