• C#设计模式:享元模式(Flyweight Pattern)


    一,什么是享元模式?

    享元模式(Flyweight Pattern):采用共享技术来避免大量拥有相同内容对象的开销,主要用于减少创建对象的数量,以减少内存占用和提高性能

    1,根本的思路就是对象的重用
    2,根本的实现逻辑就是简单工厂+静态缓存

    二,如下代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using static FlyWeightPattern.FlyweightFactory;
    
    namespace FlyWeightPattern
    {
    
        ///享元模式(FlyWeight):采用共享技术来避免大量拥有相同内容对象的开销
        // 当我们项目中创建很多对象,而且这些对象存在许多相同模块,这时,我们可以将这些相同的模块提取出来采用享元模式生成单一对象,再使用这个对象与之前的诸多对象进行配合使用,这样无疑会节省很多空间。
        class Program
        {
            static void Main(string[] args)
            {
                ////相同对象执行时,这样减少创建对象的开销,
                //for (int i = 0; i < 5; i++)
                //{
                //    Chinese ch = FlyweightFactory.GetChineseObject("中文");
                //    ch.Say();
                //}
    
                for (int i = 0; i < 5; i++)
                {
                    Task.Run(()=> {
                        People ch = FlyweightFactory.GetChineseObject(LanguageType.Chinese);
                        ch.Say();
                    });
                }
                for (int i = 0; i < 5; i++)
                {
                    Task.Run(() => {
                        People usa = FlyweightFactory.GetChineseObject(LanguageType.USA);
                        usa.Say();
                    });
                }
            }
        }
        public abstract class People
        {
            public abstract void Say();
        }
        public class Chinese : People
        {
            public Chinese()
            {
                Console.WriteLine($"{this.GetType().Name}被创建了");
            }
            public override void Say()
            {
                Console.WriteLine("中国人说:你好");
            }
        }
        public class USA : People
        {
            public USA()
            {
    
                Console.WriteLine($"{this.GetType().Name}被创建了");
            }
            public override void Say()
            {
                Console.WriteLine("USA:Hello");
            }
        }
    }

    FlyweightFactory

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace FlyWeightPattern
    {
        /// <summary>
        /// 1,根本的思路就是对象的重用
        /// 2,根本的实现逻辑就是简单工厂+静态缓存
        /// </summary>
        public class FlyweightFactory
        {
            private static Dictionary<LanguageType, People> dic = new Dictionary<LanguageType, People>(); //定义字典来保存不同的变量,相同的则取出
            public static object dic_Lock = new object();
            public static People GetChineseObject(LanguageType Language)
            {
                People people = null;
                if (!dic.ContainsKey(Language)) //先判空,多并发不需要排序
                {
                    lock (dic_Lock) ///线程锁,当多线程并发时,如果没有lock或造成dic2已经存在KEY的错误
                    {
                        if (!dic.ContainsKey(Language))   //定义字典来保存不同的变量,相同的则取出
                        {
                            switch (Language)
                            {
                                case LanguageType.Chinese:
                                    people = new Chinese();
                                    break;
                                case LanguageType.USA:
                                    people = new USA();
                                    break;
                            }
                            dic.Add(Language, people);             //我们将新创建的对象存储到缓存中
                        }
                    }
                }
                return dic[Language];
            }
            public enum LanguageType
            {
                Chinese,
                USA,
            }
        }
    }

     在多并发时的享元模式如果没有加锁的判断会出下一下的问题,字典的KEY冲突

    三,我们解析下亨元模式代码设计思路

    1,围绕减少相同对象的创建设计使我们代码设计的核心

    2,判定对象是否存在,如果存在则取出,否则就创建,这样避免对象的重复,我们通过Key来实现唯一性

     四,string的享元模式详解

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace StringDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                string str = "May";
                string str1 = "May";
                ///object.ReferenceEquals这个方法是判断内存地址是否相等
                ///根据string本身的定义,string在赋值的时候,会主动开辟一块不一样的内存空间,但是这里输出True
                ///原因:string 是使用享元模式,在赋值的时候会去堆中查找看是否存在May,如果存在str1就指向该堆中的内存地址,这是CLR内存分配机制决定的,字符串的享元是全局的,不是局部的
                Console.Write(object.ReferenceEquals(str,str1));  
    
                str = "June";
                ///按引用类型来理解,str1输出的应该是June,但是结果这里输出的是May,值不变
                ///原因:根据string本身的定义,string在赋值的时候,字符串的不可变性, str = "June"等于重新new一个string,会主动开辟一块不一样的内存空间
                Console.WriteLine(str1);
    
                string str3 = string.Format("M{0}","ay");
                ///这里输出false,因为str3在初始化中还不知道是不是May字符串,所以开辟了一块新的内存,这里不是享元模式了
                Console.WriteLine(object.ReferenceEquals(str,str3));
    
                string str4 = "M";
                string str5 =str4+ "ay";
                ///这里输出false
                Console.WriteLine(object.ReferenceEquals(str,str5));
    
                string str6 = "M" + "ay";
                ///这里输出True
                ///原因:"M" + "ay"在编译过程中,编译器自动编译成"May", 所以输出的是True
                Console.WriteLine(object.ReferenceEquals(str, str6));
    
                Console.ReadKey();
            }
        }
    }

    1,String 使用了享元模式初始化,比如我们定义两个string 都赋值是may,按道理来说string都是重新开辟内存空间的,
    2,我们用判断内存地址去判断两个值得内存地址是一样的,原因是string 初始化时去判断堆中是否有may 了,
    3,享元模式的原理,字符串享元是全局的,不是局部的,在一个进程中,只有一个堆,所以指向同一个地址的字符串是一样的

  • 相关阅读:
    django6
    django5
    欧拉回路
    消耗战合集
    小技巧
    (广义)圆方树
    最小割树(Gomory-Hu Tree)
    fhq treap
    注意!!
    急需学习的东西
  • 原文地址:https://www.cnblogs.com/May-day/p/8671051.html
Copyright © 2020-2023  润新知