【设计模式系列】享元模式

享元模式介绍

“这是我参与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);
        }
复制代码

对于享元工厂须要特地留意,它是先检索缓存池中的数据总状况,发现不是要找的,那么就新建立对象。

小寄语

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

我是阿辉,感谢您的阅读,若是对你有帮助,麻烦点赞、转发 谢谢。

相关文章
相关标签/搜索