原創文章,同步發自 "作者個人博客" "http://www.jasongj.com/design_pattern/flyweight/" 。轉載請註明出處 享元模式介紹 享元模式適用場景 面向對象技術可以很好的解決一些靈活性或可擴展性問題,但在很多情況下需要在系統中增加類和對象的個數。當對象數量太 ...
原創文章,同步發自作者個人博客 http://www.jasongj.com/design_pattern/flyweight/。轉載請註明出處
享元模式介紹
享元模式適用場景
面向對象技術可以很好的解決一些靈活性或可擴展性問題,但在很多情況下需要在系統中增加類和對象的個數。當對象數量太多時,將導致對象創建及垃圾回收的代價過高,造成性能下降等問題。享元模式通過共用相同或者相似的細粒度對象解決了這一類問題。
享元模式定義
享元模式(Flyweight Pattern),又稱輕量級模式(這也是其英文名為FlyWeight的原因),通過共用技術有效地實現了大量細粒度對象的復用。
享元模式類圖
享元模式類圖如下
享元模式角色劃分
- FlyWeight 享元介面或者(抽象享元類),定義共用介面
- ConcreteFlyWeight 具體享元類,該類實例將實現共用
- UnSharedConcreteFlyWeight 非共用享元實現類
- FlyWeightFactory 享元工廠類,控制實例的創建和共用
內部狀態 vs. 外部狀態
- 內部狀態是存儲在享元對象內部,一般在構造時確定或通過setter設置,並且不會隨環境改變而改變的狀態,因此內部狀態可以共用。
- 外部狀態是隨環境改變而改變、不可以共用的狀態。外部狀態在需要使用時通過客戶端傳入享元對象。外部狀態必須由客戶端保存。
享元模式實例解析
本文代碼可從作者Github下載
享元介面,定義共用介面
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設計模式系列
- Java設計模式(一) 簡單工廠模式不簡單
- Java設計模式(二) 工廠方法模式
- Java設計模式(三) 抽象工廠模式
- Java設計模式(四) 觀察者模式
- Java設計模式(五) 組合模式
- Java設計模式(六) 代理模式 VS. 裝飾模式
- Java設計模式(七) Spring AOP JDK動態代理 vs. cglib
- Java設計模式(八) 適配器模式
- Java設計模式(九) 橋接模式
- Java設計模式(十) 你真的用對單例模式了嗎?
- Java設計模式(十一) 享元模式