设计模式之享元
享元模式介紹
享元模式主要在于共享通用對象,減少內(nèi)存的使用,提升系統(tǒng)的訪問效率。而這部分共享對象通常比較耗費內(nèi)存或者需要查詢大量接口或者使用數(shù)據(jù)庫資源,因此統(tǒng)一抽離作為共享對象使用。
在使用此模式過程中,需要使用享元工廠來進(jìn)行管理這部分獨立的對象和共享的對象,避免出現(xiàn)線程安全的問題。
享元模式設(shè)計的思想:減少內(nèi)存的使用提升效率,和之前學(xué)習(xí)的原型模式通過克隆對象的方式生成復(fù)雜對象,減少遠(yuǎn)程系統(tǒng)的調(diào)用。
享元與不可變性
在使用享元模式時,享元對象可在不同情景中是使用,必須確保其狀態(tài)不可被修改。也就是說享元對象只能由構(gòu)造函數(shù)進(jìn)行一次性初始化,它不能對其他對象公開其設(shè)置器或共有成員變量。
享元工廠
為了更方便的訪問各種享元,可以創(chuàng)建一個工廠方法來管理已有享元對象的緩存池。
工廠方法從客戶端處接收目標(biāo)享元對象的內(nèi)在狀態(tài)作為參數(shù),如果能提前在緩存池中找到目標(biāo)享元,則直接返回。如果沒有找到,會自動創(chuàng)建一個享元對象,并將其添加到緩存池中。
享元模式的結(jié)構(gòu)
享元模式只是一種優(yōu)化,主要應(yīng)用于與大量類似對象同時占用內(nèi)存相關(guān)的內(nèi)存消耗問題時使用。
享元 ?類包含原始對象中部分能在多個對象中共享的狀態(tài)。
情景類 包含原始對象中各不相同的外在狀態(tài)。情景與享元對象組合在一起就能表示原始對象的全部狀態(tài)。
客戶端 ?負(fù)責(zé)計算或存儲享元的外在狀態(tài)。
享元工廠 會對已有享元的緩存池進(jìn)行管理。
有了工廠后,客戶端無需直接創(chuàng)建享元,它們只需調(diào)用工廠并向其傳遞目標(biāo)享元的一些內(nèi)在狀態(tài)即可。工廠會根據(jù)參數(shù)在之前已創(chuàng)建的享元中進(jìn)行查找,如果找到滿足的直接返回,若沒有則進(jìn)行創(chuàng)建新享元。
僅在程序必須支持大量對象且沒有足夠的內(nèi)存容量時使用享元模式。
程序需要生產(chǎn)數(shù)量巨大的相似對象
這將耗盡目標(biāo)設(shè)備的所有內(nèi)存
對象中包含可抽取且能在多個對象間共享的重復(fù)狀態(tài)
實現(xiàn)方式
1、將需要改寫為享元的類成員變量拆分為兩個部分
內(nèi)在狀態(tài) :包含不變的,可在許多對象中重復(fù)使用的數(shù)據(jù)的成員變量
外在狀態(tài) :包含每個對象各自不同的情景數(shù)據(jù)的成員變量
2、保留類中表示內(nèi)在狀態(tài)的成員變量,并將其屬性設(shè)置為不可修改。(這些不變的變量只能通過構(gòu)造函數(shù)進(jìn)行初始化操作)
3、找到所有使用外在狀態(tài)成員變量的方法,為在方法中所有的每個成員變量新建一個參數(shù),并使用該參數(shù)代替成員變量
4、你可以有選擇地創(chuàng)建工廠類來管理享元緩存池,它負(fù)責(zé)在新建享元時檢查已有的享元。如果選擇使用工廠,客戶端就只能通過工廠來請求享元,它們需要將享元的內(nèi)在狀態(tài)作為參數(shù)傳遞給工廠
5、客戶端必須存儲和計算外在狀態(tài)的數(shù)值,因為只有這樣才能調(diào)用享元對象的方法。外在狀態(tài)和引用享元的成員變量可以移動到單獨的情景類中。
優(yōu)點: 如果程序有很多相似的對象,那么可以節(jié)省大量的內(nèi)存。
缺點: 可能犧牲執(zhí)行速度來換取內(nèi)存、代碼會變的更加復(fù)雜。
享元展示了如何生成大量的小型對象,外觀模式則展示了如何用一個對象來代表整個子系統(tǒng)。
Demo
????///?<summary>///?享元///?</summary>public?class?Flyweight{private?Car?_sharedState;public?Flyweight(Car?car){this._sharedState?=?car;}public?void?Operation(Car?uniqueState)?{string?s?=?JsonConvert.SerializeObject(this._sharedState);string?u?=?JsonConvert.SerializeObject(uniqueState);Console.WriteLine("Flyweight:Displaying?shared?"+s+"?and?unque?"+u+"?state");}} ????///?<summary>///?享元工廠///?思路:提前在緩存池緩存對象,取值時先判斷緩存池中取,如沒有則創(chuàng)建,同時加入緩存池。///?</summary>public?class?FlyweightFactory?{private?List<Tuple<Flyweight,string>>?flyweights=new?List<Tuple<Flyweight,string>>();public?FlyweightFactory(params?Car[]?args){foreach?(var?elem?in?args){flyweights.Add(new?Tuple<Flyweight,string>(new?Flyweight(elem),this.getKey(elem)));}}public?string?getKey(Car?key)?{List<string>?elements?=?new?List<string>();elements.Add(key.Model);elements.Add(key.Color);elements.Add(key.Company);if?(key.Owner!=null&&?key.Number!=null){elements.Add(key.Number);elements.Add(key.Owner);}elements.Sort();return?string.Join("_",elements);}public?Flyweight?GetFlyweight(Car?sharedState)?{string?key?=?this.getKey(sharedState);if?(flyweights.Where(t=>t.Item2==key).Count()!=0){Console.WriteLine("在享元工廠中,緩存中沒有數(shù)據(jù)");this.flyweights.Add(new?Tuple<Flyweight,string>(new?Flyweight(sharedState),key));}else{Console.WriteLine("緩沖池中有...");}return?this.flyweights.Where(t?=>?t.Item2?==?key).FirstOrDefault().Item1;}public?void?listFlyweights()?{var?count?=?flyweights.Count;foreach?(var?item?in?flyweights){Console.WriteLine(item.Item2);}}}public?class?Car{public?string?Owner?{?get;?set;?}public?string?Number?{?get;?set;?}public?string?Company?{?get;?set;?}public?string?Model?{?get;?set;?}public?string?Color?{?get;?set;?}} ????????static?void?Main(string[]?args){var?factory?=?new?FlyweightFactory(new?Car?{?Company?=?"Chevrolet",?Model?=?"Camaro2018",?Color?=?"pink"?},new?Car?{?Company?=?"Mercedes?Benz",?Model?=?"C300",?Color?=?"black"?},new?Car?{?Company?=?"Mercedes?Benz",?Model?=?"C500",?Color?=?"red"?},new?Car?{?Company?=?"BMW",?Model?=?"M5",?Color?=?"red"?},new?Car?{?Company?=?"BMW",?Model?=?"X6",?Color?=?"white"?});factory.listFlyweights();addCarToPoliceDatabase(factory,?new?Car?{Number?=?"CL234IR",Owner?=?"James?Doe",Company?=?"BMW",Model?=?"M5",Color?=?"red"});addCarToPoliceDatabase(factory,?new?Car{Number?=?"CL234IR",Owner?=?"James?Doe",Company?=?"BMW",Model?=?"X1",Color?=?"red"});factory.listFlyweights();Console.ReadKey();}static?void?addCarToPoliceDatabase(FlyweightFactory?factory,?Car?car){Console.WriteLine("添加一個新Car");var?flyweight?=?factory.GetFlyweight(new?Car{Color?=?car.Color,Model?=?car.Model,Company?=?car.Company});flyweight.Operation(car);}對于享元工廠需要特意留意,它是先檢索緩存池中的數(shù)據(jù)總情況,發(fā)現(xiàn)不是要找的,那么就新創(chuàng)建對象。
小寄語
人生短暫,我不想去追求自己看不見的,我只想抓住我能看的見的。
我是阿輝,感謝您的閱讀,如果對你有幫助,麻煩點贊、轉(zhuǎn)發(fā) ?謝謝。
總結(jié)
- 上一篇: 在VS Code中直接调试Web程序,是
- 下一篇: 在 ASP.NET Core 中使用 S