【設計模式系列】享元模式

享元模式介紹

「這是我參與8月更文挑戰的第9天,活動詳情查看:8月更文挑戰數據庫

享元模式主要在於共享通用對象,減小內存的使用,提高系統的訪問效率。而這部分共享對象一般比較耗費內存或者須要查詢大量接口或者使用數據庫資源,所以統一抽離做爲共享對象使用。緩存

在使用此模式過程當中,須要使用享元工廠來進行管理這部分獨立的對象和共享的對象,避免出現線程安全的問題。安全

享元模式設計的思想:減小內存的使用提高效率,和以前學習的原型模式經過克隆對象的方式生成複雜對象,減小遠程系統的調用。markdown

享元與不可變性函數

在使用享元模式時,享元對象可在不一樣情景中是使用,必須確保其狀態不可被修改。也就是說享元對象只能由構造函數進行一次性初始化,它不能對其餘對象公開其設置器或共有成員變量。post

享元工廠學習

爲了更方便的訪問各類享元,能夠建立一個工廠方法來管理已有享元對象的緩存池。優化

工廠方法從客戶端處接收目標享元對象的內在狀態做爲參數,若是能提早在緩存池中找到目標享元,則直接返回。若是沒有找到,會自動建立一個享元對象,並將其添加到緩存池中。this

享元模式的結構

  • 享元模式只是一種優化,主要應用於與大量相似對象同時佔用內存相關的內存消耗問題時使用。spa

  • 享元 類包含原始對象中部分能在多個對象中共享的狀態。

  • 情景類 包含原始對象中各不相同的外在狀態。情景與享元對象組合在一塊兒就能表示原始對象的所有狀態。

  • 客戶端 負責計算或存儲享元的外在狀態。

  • 享元工廠 會對已有享元的緩存池進行管理。

有了工廠後,客戶端無需直接建立享元,它們只需調用工廠並向其傳遞目標享元的一些內在狀態便可。工廠會根據參數在以前已建立的享元中進行查找,若是找到知足的直接返回,若沒有則進行建立新享元。

僅在程序必須支持大量對象且沒有足夠的內存容量時使用享元模式。

  • 程序須要生產數量巨大的類似對象
  • 這將耗盡目標設備的全部內存
  • 對象中包含可抽取且能在多個對象間共享的重複狀態

實現方式

一、將須要改寫爲享元的類成員變量拆分爲兩個部分

  • 內在狀態 : 包含不變的,可在許多對象中重複使用的數據的成員變量
  • 外在狀態 : 包含每一個對象各自不一樣的情景數據的成員變量

二、保留類中表示內在狀態的成員變量,並將其屬性設置爲不可修改。(這些不變的變量只能經過構造函數進行初始化操做)

三、找到全部使用外在狀態成員變量的方法,爲在方法中全部的每一個成員變量新建一個參數,並使用該參數代替成員變量

四、你能夠有選擇地建立工廠類來管理享元緩存池,它負責在新建享元時檢查已有的享元。若是選擇使用工廠,客戶端就只能經過工廠來請求享元,它們須要將享元的內在狀態做爲參數傳遞給工廠

五、客戶端必須存儲和計算外在狀態的數值,由於只有這樣才能調用享元對象的方法。外在狀態和引用享元的成員變量能夠移動到單獨的情景類中。

優勢: 若是程序有不少類似的對象,那麼能夠節省大量的內存。

缺點: 可能犧牲執行速度來換取內存、代碼會變的更加複雜。

享元展現瞭如何生成大量的小型對象,外觀模式則展現瞭如何用一個對象來表明整個子系統。

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);
        }
複製代碼

對於享元工廠須要特地留意,它是先檢索緩存池中的數據總狀況,發現不是要找的,那麼就新建立對象。

小寄語

人生短暫,我不想去追求本身看不見的,我只想抓住我能看的見的。

我是阿輝,感謝您的閱讀,若是對你有幫助,麻煩點贊、轉發 謝謝。

相關文章
相關標籤/搜索