设计模式之享元
享元模式介紹
享元模式主要在于共享通用對象,減少內存的使用,提升系統的訪問效率。而這部分共享對象通常比較耗費內存或者需要查詢大量接口或者使用數據庫資源,因此統一抽離作為共享對象使用。
在使用此模式過程中,需要使用享元工廠來進行管理這部分獨立的對象和共享的對象,避免出現線程安全的問題。
享元模式設計的思想:減少內存的使用提升效率,和之前學習的原型模式通過克隆對象的方式生成復雜對象,減少遠程系統的調用。
享元與不可變性
在使用享元模式時,享元對象可在不同情景中是使用,必須確保其狀態不可被修改。也就是說享元對象只能由構造函數進行一次性初始化,它不能對其他對象公開其設置器或共有成員變量。
享元工廠
為了更方便的訪問各種享元,可以創建一個工廠方法來管理已有享元對象的緩存池。
工廠方法從客戶端處接收目標享元對象的內在狀態作為參數,如果能提前在緩存池中找到目標享元,則直接返回。如果沒有找到,會自動創建一個享元對象,并將其添加到緩存池中。
享元模式的結構
享元模式只是一種優化,主要應用于與大量類似對象同時占用內存相關的內存消耗問題時使用。
享元 ?類包含原始對象中部分能在多個對象中共享的狀態。
情景類 包含原始對象中各不相同的外在狀態。情景與享元對象組合在一起就能表示原始對象的全部狀態。
客戶端 ?負責計算或存儲享元的外在狀態。
享元工廠 會對已有享元的緩存池進行管理。
有了工廠后,客戶端無需直接創建享元,它們只需調用工廠并向其傳遞目標享元的一些內在狀態即可。工廠會根據參數在之前已創建的享元中進行查找,如果找到滿足的直接返回,若沒有則進行創建新享元。
僅在程序必須支持大量對象且沒有足夠的內存容量時使用享元模式。
程序需要生產數量巨大的相似對象
這將耗盡目標設備的所有內存
對象中包含可抽取且能在多個對象間共享的重復狀態
實現方式
1、將需要改寫為享元的類成員變量拆分為兩個部分
內在狀態 :包含不變的,可在許多對象中重復使用的數據的成員變量
外在狀態 :包含每個對象各自不同的情景數據的成員變量
2、保留類中表示內在狀態的成員變量,并將其屬性設置為不可修改。(這些不變的變量只能通過構造函數進行初始化操作)
3、找到所有使用外在狀態成員變量的方法,為在方法中所有的每個成員變量新建一個參數,并使用該參數代替成員變量
4、你可以有選擇地創建工廠類來管理享元緩存池,它負責在新建享元時檢查已有的享元。如果選擇使用工廠,客戶端就只能通過工廠來請求享元,它們需要將享元的內在狀態作為參數傳遞給工廠
5、客戶端必須存儲和計算外在狀態的數值,因為只有這樣才能調用享元對象的方法。外在狀態和引用享元的成員變量可以移動到單獨的情景類中。
優點: 如果程序有很多相似的對象,那么可以節省大量的內存。
缺點: 可能犧牲執行速度來換取內存、代碼會變的更加復雜。
享元展示了如何生成大量的小型對象,外觀模式則展示了如何用一個對象來代表整個子系統。
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>///?享元工廠///?思路:提前在緩存池緩存對象,取值時先判斷緩存池中取,如沒有則創建,同時加入緩存池。///?</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("在享元工廠中,緩存中沒有數據");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);}對于享元工廠需要特意留意,它是先檢索緩存池中的數據總情況,發現不是要找的,那么就新創建對象。
小寄語
人生短暫,我不想去追求自己看不見的,我只想抓住我能看的見的。
我是阿輝,感謝您的閱讀,如果對你有幫助,麻煩點贊、轉發 ?謝謝。
總結
- 上一篇: 在VS Code中直接调试Web程序,是
- 下一篇: 在 ASP.NET Core 中使用 S