#享元模式(Flyweight Pattern)
##簡介
享元模式(英語:Flyweight Pattern)是一種軟件設計模式。它使用共享物件,用來盡可能減少內存使用量以及分享資訊給盡可能多的相似物件;它適合用于當大量物件只是重復因而導致無法令人接受的使用大量內存。通常物件中的部分狀態是可以分享。常見做法是把它們放在外部數據結構,當需要使用時再將它們傳遞給享元。
典型的享元模式的例子為文書處理器中以圖形結構來表示字符。一個做法是,每個字形有其字型外觀, 字模 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<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);
// Retrieve previously created instance with the given values if it (still) exists
WeakReference<FontData> 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<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.
}
```
結構圖

時序圖

##實例
###第一版
```
//網站
class WebSite
{
private string name = "";
private WebSite(string name)
{
this.name = name;
}
public void Use()
{
Console.WriteLine("網站分類" + name);
}
}
```
客戶端代碼
```
static void Main(string args)
{
WebSite fx = new WebSite("產品展示");
fx.Use();
WebSite fy = new WebSite("產品展示");
fy.Use();
WebSite f1 = new WebSite("博客");
f1.Use();
WebSite f2 = new WebSite("博客");
f2.Use();
Console.Read();
}
```
###第二版
網站抽象類
```
abstract class WebSite
{
public abstract void Use();
}
```
具體網站類
```
class ConcreteWebSite :WebSite
{
private string name = "";
public ConcreteWebSite(string name)
{
this.name =name;
}
public override void Use()
{
Console.WriteLine("網站分類:" + name);
}
}
```
網站工廠類
```
//網站工廠
class WebSiteFactory
{
private HashTable flyweights = new Hashtable();
//獲得網站分類
public WebSite GetWebSiteCategory(string key)
{
if (!flyweight.ContainKey(key))
flyweights.Add(key, new ConcreteWebSite(key));
return ((WebSite)flyweights[key]);
}
//獲得網站分類總數
public int GetWebSiteCount()
{
return flyweights.Count;
}
}
```
客戶端代碼
```
static void Main(string[] args)
{
WebSiteFactory f = new WebSiteFactory();
WebSite fx = f.GetWebSiteCategory("產品展示");
fx.Use();
WebSite fy = f.GetWebSiteCategory("產品展示");
fy.Use();
WebSite f1 = f.GetWebSiteCategory("博客");
f1.Use();
WebSite f2 = f.GetWebSiteCategory("博客");
f2.Use();
}
```
***實際上,享元模式可以避免大量非常詳細類的開銷。在程序設計中,有時需要生成大量細力度的類實例來表示數據。如果能發現這些實例除了幾個參數外基本上都是相同的,有時就能夠受大幅度地減少需要實例化的類的數量。如果能把這些蠶食移到類實例的外面,在方法調用時將它們傳遞進來,就可以通過共享大幅度地減少單個實例的數目。***
###第三版
用戶類,用于網站的客戶端賬號,是"網站"類的外部狀態
```
//用戶
public class User
{
private string name;
public User(string name)
{
this.name = name;
}
public string name
{
get {return name;}
}
}
```
網站抽象類
```
abstract class WebSite
{
public abstract void Use(User user);
}
```
具體網站類
```
class ConcreteWebSite: WebSite
{
private string name = "";
public ConcreteWebSite(string name)
{
this.name = name;
}
public override void Use(Use user)
{
Console.WriteLine("網站分類:" + name + "用戶:" + user.name);
}
}
```
網站工廠類
```
class WebSiteFactory
{
private Hashtable flyweights = new HashTable();
//獲得網站分類
public WebSite GetWebSiteCateegory(string key)
{
if (!flyweights.ContainsKey(key))
flyweights.Add(key, new ConcreteWebSite(key));
return ((webSite)flyweightskey[key]);
}
//獲得網站分類總數
public int GetWebSiteCount
{
return flyweights.Count;
}
}
```
客戶端代碼
```
static void Main(string[] args)
{
WebSiteFactory f = new WebSiteFactory();
WebSite fx = f.GetWebSiteCategory("產品展示");
fx.Use(new User("小菜"));
WebSite fy = f.GetWebSiteCategory("產品展示");
fy.Use(new User("小白"));
WebSite f1 = f.GetWebSiteCategory("博客");
f1.Use(new User("小黑"));
WebSite f2 = f.GetWebSiteCategory("博客");
f2.Use(new User("小綠"));
Console.Read();
}
```
***如果一個應用程序使用了大量的對象,而大量噠這些對象造成了很大的儲存開銷時就應該考慮使用;還有就是對象的大多數狀態可以外部狀態,如果刪除對象的外部狀態,那么可以用相對較少的共享對象取代很多組對象,此時可以考慮使用享元模式。***
##經典案例
###Cell的重用

在使用UITableView的時候我們應該熟悉這樣的接口:
```
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
//ios6
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath
```
在要使用一個Cell的時候我們先去看看tableView中有沒有可以重用的cell,如果有就用這個可以重用的cell,只有在沒有的時候才去創建一個Cell。這就是享元模式。
享元模式可以理解成,當細粒度的對象數量特別多的時候運行的代價會相當大,此時運用共享的技術來大大降低運行成本。比較突出的表現就是內容有效的抑制內存抖動的情況發生,還有控制內存增長。它的英文名字是flyweight,讓重量飛起來。哈哈。名副其實,在一個TableView中Cell是一個可重復使用的元素,而且往往需要布局的cell數量很大。如果每次使用都創建一個Cell對象,系統的內容抖動會非常明顯,而且系統的內存消耗也是比較大的。突然一想,享元模式只是給對象實例共享提供了一個比較霸道的名字吧。
```
- (DZTableViewCell*) dzTableView:(DZTableView *)tableView cellAtRow:(NSInteger)row
{
static NSString* const cellIdentifiy = @"detifail";
DZTypeCell* cell = (DZTypeCell*)[tableView dequeueDZTalbeViewCellForIdentifiy:cellIdentifiy];
if (!cell) {
cell = [[DZTypeCell alloc] initWithIdentifiy:cellIdentifiy];
}
NSString* text = _timeTypes[row];
return cell;
}
```