概述:
在項目開發中我們有時候需要控制一些細密度的集合類,通常的做法是系統實例化每一個類進行指定的業務操作,這時系統需要消耗很多的內存,如果類過多的話將會把內存給撐爆掉。有種做法就是不用面向對象,呵呵對!的確可以但這個和你整個系統采用面向對象程序設計又有些格格不入。那么我們如何避免大量細粒度的對象,同時又不影響客戶程序使用面向對象的操作呢?我們先來看個簡單的超市進銷存的例子(這里我精簡了一些,列出了部分對象屬性。)
需求:將商品做出倉入倉管理
需求很簡單,打個比方,最近超市蛋黃派(汗!又是蛋黃派。沒辦法本人天天早餐就是一個蛋黃派)賣的比較火超市倉庫出貨進貨比較頻繁。正常的設計是有一個蛋黃派我就實例化一個,然后存入倉庫。這里就出現了很大的問題,如果有 1000 袋要入倉 1000 袋要出倉那我們要實例化 2000 個這樣的類,這樣對內存消耗和處理性能來說都是有很大負擔的。這時我們需要一種方式來解決這個問題,享元模式就是專門處理這樣的情況的。享元模式用于處理大量細粒度的對象。那么我們就用它來處理上面的問題
看下圖:
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 248.25pt; HEIGHT: 317.25pt" type="#_x0000_t75"><imagedata o:title="flayWeigth-1" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.jpg"></imagedata></shape>
解釋:
1. 定義一個 commodity 商品,對于倉庫來講 他們統計商品只需要知道每種商品的條形碼和庫存量,至于其他的屬性沒有必要一一了解。
2. 定義一個蛋黃派類實現 commodity 抽象類中的 in 、 out 方法
3. 定義一個商品供應商 CakeAccommodate 他組要負責向超市供應商品,當供應商沒有經營一個商品時他會立刻建立這個商品的采購渠道保證商品的正常供給
這里也許大家會問怎么說了半天倉庫,倉庫這個類都沒有,這里我做了簡化,如果你愿意可以再建一個倉庫類和超市類出來,我這里將 commodity 設計成了一個存放商品的空間,可能名字取的不好吧!如果你想完善他可以自行設計一個,這里我只是圖了個方便省事,首先在在這里聲明這不是一個很好的對象設計 . 只是為了說明享元模式。 Commodity 這里你可以理解為存放某類商品的存儲空間
代碼:
private static void SimplenFlyWeigth()
{
CakeAccommodate acc = new CakeAccommodate ();
Commodity gm = acc.GetCakes( " 光明 " );
gm.In(20);
Commodity wg = acc.GetCakes( " 衛崗 " );
wg.In(25);
Commodity wm = acc.GetCakes( " 無名 " );
wm.In(10);
gm.Out(30);
gm.In(10);
gm.Out(30);
wg.Out(5);
wm.Out(1);
//Console.ReadLine();
}
效果 :
<shape id="_x0000_i1026" style="WIDTH: 414.75pt; HEIGHT: 276pt" type="#_x0000_t75"><imagedata o:title="flayWeigth-3" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image002.jpg"></imagedata></shape>
但真正的倉儲沒那么簡單,往往每批貨物都是規整放到指定位置的,那么這樣就需要我們依據不同的位置存放商品,這里我們引入復合享元模式的機制。通過傳入參數動態控制細粒度對象的業務邏輯
看下圖:
<shape id="_x0000_i1027" style="WIDTH: 374.25pt; HEIGHT: 335.25pt" type="#_x0000_t75"><imagedata o:title="flayWeigth-2" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image004.jpg"></imagedata></shape>
1. PointCommodity :指定的某種類存儲位置
2. ShelfPoint :貨架
3. PointCake :存放在指定貨架上的蛋黃派
4. CakeAccommodate :蛋黃派供應商
運行效果:
<shape id="_x0000_i1028" style="WIDTH: 415.5pt; HEIGHT: 277.5pt" type="#_x0000_t75"><imagedata o:title="flayWeigth-4" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image005.jpg"></imagedata></shape>
OOD 設計合理性:
1. 是否符合開不原則:
如果我們需要再加一種商品進來的話我們只需要新建一個類繼承 Commodity 抽象 類就可以了,不用修改其他類
符合
2. 是否符合里氏代換:
符合
3. 是否符合抽象原則
符合
4. 是否符合迪米特法則
符合
享元模式總結
意圖:
運用共享技術有效地支持大量細粒度的對象
結構圖:
<shape id="_x0000_i1029" style="WIDTH: 321.75pt; HEIGHT: 201pt" type="#_x0000_t75"><imagedata o:title="flayWeigth-5" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image007.jpg"></imagedata></shape>
享元模式所涉及的角色有抽象享元角色、具體享元角色、復合享元角色、享員工廠角色,以及客戶端角色等。
抽象享元角色: 此角色是所有的具體享元類的超類,為這些類規定出需要實現的公共接口。那些需要外蘊狀態 (External State) 的操作可以通過方法的參數傳入。抽象享元的接口使得享元變得可能,但是并不強制子類實行共享,因此并非所有的享元對象都是可以共享的。
具體享元 (ConcreteFlyweight) 角色: 實現抽象享元角色所規定的接口。如果有內蘊狀態的話,必須負責為內蘊狀態提供存儲空間。享元對象的內蘊狀態必須與對象所處的周圍環境無關,從而使得享元對象可以在系統內共享。有時候具體享元角色又叫做單純具體享元角色,因為復合享元角色是由單純具體享元角色通過復合而成的。
復合享元 (UnsharableFlyweight) 角色: 復合享元角色所代表的對象是不可以共享的,但是一個復合享元對象可以分解成為多個本身是單純享元對象的組合。復合享元角色又稱做不可共享的享元對象。
享元工廠 (FlyweightFactoiy) 角色: 本角色負責創建和管理享元角色。本角色必須保證享元對象可以被系統適當地共享。當一個客戶端對象請求一個享元對象的時候,享元工廠角色需要檢查系統中是否已經有一個符合要求的享元對象,如果已經有了,享元工廠角色就應當提供這個已有的享元對象;如果系統中沒有一個適當的享元對象的話,享元工廠角色就應當創建一個新的合適的享元對象。
客戶端 (Client) 角色: 本角色還需要自行存儲所有享元對象的外蘊狀態
使用背景:
1. 一個系統有大量的對象。
2. 這些對象耗費大量的內存。
3. 這些對象的狀態中的大部分都可以外部化。
4. 這些對象可以按照內蘊狀態分成很多的組,當把外蘊對象從對象中剔除時,每一個組都可以僅用一個對象代替。
5. 軟件系統不依賴于這些對象的身份,換言之,這些對象可以是不可分辨的。
模式優缺點:
優點:
1. 大大節約了存儲空間
備注:
Flyweight 在拳擊比賽中指最輕量級,即 " 蠅量級 " ,有些作者翻譯為 " 羽量級 " 。這里使用 " 享元模式 " 更能反映模式的用意。
享元模式以共享的方式高效地支持大量的細粒度對象。享元對象能做到共享的關鍵是區分內蘊狀態( Internal State )和外蘊狀態( External State )。內蘊狀態是存儲在享元對象內部并且不會隨環境改變而改變。因此內蘊狀態并可以共享。
外蘊狀態是隨環境改變而改變的、不可以共享的狀態。享元對象的外蘊狀態必須由客戶端保存,并在享元對象被創建之后,在需要使用的時候再傳入到享元對象內部。外蘊狀態與內蘊狀態是相互獨立的。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
