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,享元模式的原理,字符串享元是全局的,不是局部的,在一个进程中,只有一个堆,所以指向同一个地址的字符串是一样的