享元模式(Flyweight)

jdon發表於2019-07-16

目的
使用共享可以有效地支援大量細粒度物件。

說明
鍊金術士的商店裡擺滿了魔法藥水。許多藥水是相同的,因此不需要為每個藥水建立新的物件。相反,一個物件例項可以表示多個貨架專案,因此記憶體佔用空間很小

簡而言之
它用於透過儘可能多地與類似物件共享來使記憶體使用最小化。

原始碼示例
從上面翻譯我們的鍊金術商店示例。首先,我們有不同的藥水型別

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。

相關文章