1. 程式人生 > >Java設計模式(十一) 享元模式

Java設計模式(十一) 享元模式

享元模式介紹

享元模式適用場景

面向物件技術可以很好的解決一些靈活性或可擴充套件性問題,但在很多情況下需要在系統中增加類和物件的個數。當物件數量太多時,將導致物件建立及垃圾回收的代價過高,造成效能下降等問題。享元模式通過共享相同或者相似的細粒度物件解決了這一類問題。

享元模式定義

享元模式(Flyweight Pattern),又稱輕量級模式(這也是其英文名為FlyWeight的原因),通過共享技術有效地實現了大量細粒度物件的複用。

享元模式類圖

享元模式類圖如下
FlyWeight Pattern Class Diagram

享元模式角色劃分

  • FlyWeight 享元介面或者(抽象享元類),定義共享介面
  • ConcreteFlyWeight
    具體享元類,該類例項將實現共享
  • UnSharedConcreteFlyWeight 非共享享元實現類
  • FlyWeightFactory 享元工廠類,控制例項的建立和共享

內部狀態 vs. 外部狀態

  • 內部狀態是儲存在享元物件內部,一般在構造時確定或通過setter設定,並且不會隨環境改變而改變的狀態,因此內部狀態可以共享。
  • 外部狀態是隨環境改變而改變、不可以共享的狀態。外部狀態在需要使用時通過客戶端傳入享元物件。外部狀態必須由客戶端儲存。

享元模式例項解析

享元介面,定義共享介面

package com.jasongj.flyweight;

public interface
FlyWeight {
void action(String externalState); }

具體享元類,實現享元介面。該類的物件將被複用

package com.jasongj.flyweight;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ConcreteFlyWeight implements FlyWeight {

  private static final Logger LOG = LoggerFactory.getLogger(ConcreteFlyWeight.class);

  private
String name; public ConcreteFlyWeight(String name) { this.name = name; } @Override public void action(String externalState) { LOG.info("name = {}, outerState = {}", this.name, externalState); } }

享元模式中,最關鍵的享元工廠。它將維護已建立的享元例項,並通過例項標記(一般用內部狀態)去索引對應的例項。當目標物件未建立時,享元工廠負責建立例項並將其加入標記-物件對映。當目標物件已建立時,享元工廠直接返回已有例項,實現物件的複用。

package com.jasongj.factory;

import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jasongj.flyweight.ConcreteFlyWeight;
import com.jasongj.flyweight.FlyWeight;

public class FlyWeightFactory {

  private static final Logger LOG = LoggerFactory.getLogger(FlyWeightFactory.class);

  private static ConcurrentHashMap<String, FlyWeight> allFlyWeight = new ConcurrentHashMap<String, FlyWeight>();

  public static FlyWeight getFlyWeight(String name) {
    if (allFlyWeight.get(name) == null) {
      synchronized (allFlyWeight) {
        if (allFlyWeight.get(name) == null) {
          LOG.info("Instance of name = {} does not exist, creating it");
          FlyWeight flyWeight = new ConcreteFlyWeight(name);
          LOG.info("Instance of name = {} created");
          allFlyWeight.put(name, flyWeight);
        }
      }
    }
    return allFlyWeight.get(name);
  }

}

從上面程式碼中可以看到,享元模式中物件的複用完全依靠享元工廠。同時本例中實現了物件建立的懶載入。並且為了保證執行緒安全及效率,本文使用了雙重檢查(Double Check)。

本例中,name可以認為是內部狀態,在構造時確定。externalState屬於外部狀態,由客戶端在呼叫時傳入。

享元模式分析

享元模式優點

  • 享元模式的外部狀態相對獨立,使得物件可以在不同的環境中被複用(共享物件可以適應不同的外部環境)
  • 享元模式可共享相同或相似的細粒度物件,從而減少了記憶體消耗,同時降低了物件建立與垃圾回收的開銷

享元模式缺點

  • 外部狀態由客戶端儲存,共享物件讀取外部狀態的開銷可能比較大
  • 享元模式要求將內部狀態與外部狀態分離,這使得程式的邏輯複雜化,同時也增加了狀態維護成本

享元模式已(未)遵循的OOP原則

已遵循的OOP原則

  • 依賴倒置原則
  • 迪米特法則
  • 里氏替換原則
  • 介面隔離原則
  • 單一職責原則
  • 開閉原則

未遵循的OOP原則

  • NA

Java設計模式系列