享元模式
外觀
它是一種機制,透過它可以避免建立大量物件例項來表示整個系統。為了決定程式的某些部分是否適合使用享元模式,請考慮是否可以從類中刪除一些資料並將其設為外部的。
示例

享元模式的經典用例是字處理器中字元圖形表示的資料結構。可能需要為文件中的每個字元提供一個包含其字型輪廓、字型度量和其他格式化資料的字形物件,但這將導致每個字元佔用數百或數千位元組。相反,每個字元可能有一個指向由文件中相同字元的所有例項共享的享元字形物件的引用;只有每個字元的位置(在文件和/或頁面中)需要在內部儲存。另一個例子是字串駐留。

在電子遊戲中,通常必須多次顯示相同的精靈(即遊戲物品的影像)。如果每個精靈都是不同的物件,則會極大地使用 CPU 和記憶體。因此,精靈只建立一次,然後在螢幕的不同位置渲染。這個問題可以使用享元模式解決。渲染精靈的物件就是一個享元。
成本
此模式有幾種實現方式。因此,找到廉價的實現方式取決於您。只有在您存在或將要遇到 CPU 或記憶體問題時才實施此模式。
建立
此模式建立起來非常容易。
維護
此模式維護起來非常容易。
刪除
此模式刪除起來也很容易。
建議
- 使用語言中現有的工具,例如 Java 中的集合。
實現
Java 中的實現
以下程式說明了上面給出的文件示例:享元在 Java 示例中被稱為 FontData。這些示例說明了享元模式用於透過僅將執行某些立即任務所需的必要資料從大型 Font 物件載入到更小的 FontData(享元)物件中來減少記憶體。
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.awt.Color;
public final class FontData {
enum FontEffect {
BOLD, ITALIC, SUPERSCRIPT, SUBSCRIPT, STRIKETHROUGH
}
/**
* 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<FontData, WeakReference<FontData>> FLY_WEIGHT_DATA =
new WeakHashMap<FontData, WeakReference<FontData>>();
private final int pointSize;
private final String fontFace;
private final Color color;
private final Set<FontEffect> effects;
private FontData(int pointSize, String fontFace, Color color, EnumSet<FontEffect> 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<FontEffect> 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);
FontData result = null;
// Retrieve previously created instance with the given values if it (still) exists
WeakReference<FontData> ref = FLY_WEIGHT_DATA.get(data);
if (ref != null) {
result = ref.get();
}
// Store new font data instance if no matching instance exists
if(result == null){
FLY_WEIGHT_DATA.put(data, new WeakReference<FontData> (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.
}
Python 中的實現
'''http://codesnipers.com/?q=python-flyweights'''
from __future__ import print_function
import weakref
class Card(object):
# comment __new__ and uncomment __init__ to see the difference
'''The object pool. Has builtin reference counting'''
_CardPool = weakref.WeakValueDictionary()
'''If the object exists in the pool just return it (instead of creating a new one)'''
def __new__(cls, value, suit):
obj = Card._CardPool.get(value + suit, None)
if not obj:
obj = object.__new__(cls)
Card._CardPool[value + suit] = obj
obj.value, obj.suit = value, suit
return obj
# def __init__(self, value, suit):
# self.value, self.suit = value, suit
def __repr__(self):
return "<Card: %s%s>" % (self.value, self.suit)
if __name__ == '__main__':
c1 = Card('9', 'h')
c2 = Card('9', 'h')
print(c1, c2)
print(c1 == c2)
print(id(c1), id(c2))
