享元模式
外观
它是一种机制,通过它可以避免创建大量对象实例来表示整个系统。为了决定程序的某些部分是否适合使用享元模式,请考虑是否可以从类中删除一些数据并将其设为外部的。
示例
享元模式的经典用例是字处理器中字符图形表示的数据结构。可能需要为文档中的每个字符提供一个包含其字体轮廓、字体度量和其他格式化数据的字形对象,但这将导致每个字符占用数百或数千字节。相反,每个字符可能有一个指向由文档中相同字符的所有实例共享的享元字形对象的引用;只有每个字符的位置(在文档和/或页面中)需要在内部存储。另一个例子是字符串驻留。
在电子游戏中,通常必须多次显示相同的精灵(即游戏物品的图像)。如果每个精灵都是不同的对象,则会极大地使用 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))