享元模式

享元模式

享元模式(英語:Flyweight Pattern)是一種軟體設計模式。它使用共享物件,用來儘可能減少內存使用量以及分享資訊給儘可能多的相似物件;它適合用於只是因重複而導致使用無法令人接受的大量內存的大量物件。通常物件中的部分狀態是可以分享。常見做法是把它們放在外部數據結構,當需要使用時再將它們傳遞給享元。

結構


兩個狀態

內蘊狀態存儲在享元內部,不會隨環境的改變而有所不同,是可以共享的
外蘊狀態是不可以共享的,它隨環境的改變而改變的,因此外蘊狀態是由客戶端來保持(因為環境的變化是由客戶端引起的)。

UML結構圖

享元模式
享元模式
(1) 抽象享元角色:為具體享元角色規定了必須實現的方法,而外蘊狀態就是以參數的形式通過此方法傳入。在Java中可以由抽象類、介面來擔當。
(2) 具體享元角色:實現抽象角色規定的方法。如果存在內蘊狀態,就負責為內蘊狀態提供存儲空間。
(3) 享元工廠角色:負責創建和管理享元角色。要想達到共享的目的,這個角色的實現是關鍵!
(4) 客戶端角色:維護對所有享元對象的引用,而且還需要存儲對應的外蘊狀態。

使用場景


如果一個應用程序使用了大量的對象,而這些對象造成了很大的存儲開銷的時候就可以考慮是否可以使用享元模式。
例如,如果發現某個對象的生成了大量細粒度的實例,並且這些實例除了幾個參數外基本是相同的,如果把那些共享參數移到類外面,在方法調用時將他們傳遞進來,就可以通過共享大幅度單個實例的數目。

示例


典型的享元模式的例子為文書處理器中以圖形結構來表示字元。一個做法是,每個字形有其字型外觀, 字模 metrics, 和其它格式資訊,但這會使每個字元就耗用上千位元組。取而代之的是,每個字元參照到一個共享字形物件,此物件會被其它有共同特質的字元所分享;只有每個字元(文件中或頁面中)的位置才需要另外儲存。以下程式用來解釋上述的文件例子。這個例子用來解釋享元模式利用只載立執行立即小任務所必需的資料,因而減少內存使用量。
public enum FontEffect {
BOLD, ITALIC, SUPERSCRIPT, SUBSCRIPT, STRIKETHROUGH
}
public final class FontData {
/**
* A weak hash map will drop unused references to FontData.
* Values have to be wrapped in WeakReferences,
* because value objects in weak hash map are held by strong references. *
/
private static final WeakHashMap> FLY_WEIGHT_DATA = new WeakHashMap>();
private final int pointSize;
private final String fontFace;
private final Color color;
private final Set effects;
private FontData(int pointSize, String fontFace, Color color, EnumSet effects){ this.pointSize = pointSize;
this.fontFace = fontFace;
this.color = color;
this.effects = Collections.unmodifiableSet(effects);
}
public static FontData create(int pointSize, String fontFace, Color color, FontEffect... effects) { EnumSet effectsSet = EnumSet.noneOf(FontEffect.class);
for (FontEffect fontEffect : effects) {
effectsSet.add(fontEffect);
}
// We are unconcerned with object creation cost, we are reducing overall memory consumption FontData data = new FontData(pointSize, fontFace, color, effectsSet);
// Retrieve previously created instance with the given values if it (still) exists WeakReference ref = FLY_WEIGHT_DATA.get(data);
FontData result = (ref != null) ? ref.get() : null; // Store new font data instance if no matching instance exists
if (result == null) {
FLY_WEIGHT_DATA.put(data, new WeakReference (data));
result = data;
}
// return the single immutable copy with the given values
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FontData) {
if (obj == this) {
return true;
}
FontData other = (FontData) obj;
return other.pointSize == pointSize && other.fontFace.equals(fontFace) && other.color.equals(color) && other.effects.equals(effects);
}
return false;
}
@Override
public int hashCode() {
return (pointSize * 37 + effects.hashCode() * 13) * fontFace.hashCode();
}
// Getters for the font data, but no setters. FontData is immutable.
}