享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;
它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。
常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。他与单例最大的特征就是单例的实例类是不允许在外部构建的,
但是享元模式可以,享元模式的适用场景很多,例如线程池,数据连接池,包括CLR就也调用它
现在我们先来实现享元模式,创建几个简单的类
1 public class E : BaseWord
2 {
3 public E()
4 {
5 Thread.Sleep(1000);
6 Console.WriteLine("字符E被构造");
7 }
8
9 public override string Display()
10 {
11 return "E";
12 }
13 }
14
15 public class L : BaseWord
16 {
17 public L()
18 {
19 Thread.Sleep(1000);
20 Console.WriteLine("字符L被构造");
21 }
22
23 public override string Display()
24 {
25 return "L";
26 }
27
28 }
29
30 public class N : BaseWord
31 {
32 public N()
33 {
34 Thread.Sleep(1000);
35 Console.WriteLine("字符N被构造");
36 }
37
38 public override string Display()
39 {
40 return "N";
41 }
42 }
43
44 public class V : BaseWord
45 {
46 public V()
47 {
48 Thread.Sleep(1000);//表明对象构造比较耗时
49 Console.WriteLine("字符V被构造");
50 }
51
52 public override string Display()
53 {
54 return "V";
55 }
56 }
然后新建一个FlyweightPattern类
1 namespace 享元模式
2 {
3 public class FlyweightPattern
4 {
5 private static Dictionary<WordType, BaseWord> _BaseWordDic = new Dictionary<WordType, BaseWord>();
6 private static object GetWord_Lock = new object();
7
8 public static BaseWord GetWord(WordType wordType)
9 {
10 BaseWord baseword = null;
11 //双if+Lock
12 if (_BaseWordDic.ContainsKey(wordType))
13 {
14 return _BaseWordDic[wordType];
15 }
16 else
17 {
18 lock (GetWord_Lock)
19 {
20 if (_BaseWordDic.ContainsKey(wordType))
21 {
22 baseword = _BaseWordDic[wordType];
23 }
24 else
25 {
26 switch (wordType)
27 {
28 case WordType.E:
29 baseword = new E();
30 break;
31 case WordType.L:
32 baseword = new L();
33 break;
34 case WordType.V:
35 baseword = new V();
36 break;
37 case WordType.N:
38 baseword = new N();
39 break;
40 default:
41 throw new Exception("wrong wordType");
42 }
43 }
44 _BaseWordDic[wordType] = baseword;
45 return baseword;
46 }
47 }
48 }
49 }
50
51 public enum WordType
52 {
53 E,
54 L,
55 V,
56 N
57 }
58 }
享元模式使用一个静态的字典来保存已经实例化出来的类的值,下次进来的时候会先判断字典,大大减少了创建实例的性能开销。
使用双if+lock,确保在多线程下也能正常的运行
![](https://i-blog.csdnimg.cn/blog_migrate/9d4cc24276920f0d3dd0f4a8f0e55f16.png)
然后我们写上这两个相同的方法来测试,使用异步的方法来调用
![](https://i-blog.csdnimg.cn/blog_migrate/775a855958c076aeea4bcf5caa63b6e3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/02ff2e4828e092c4823c0c9d2d43f4f0.png)
在多次调用该类时,不会被创建,而是通过享元模式更快的获得了想要的类的对象
接下来用另一个方法来说明享元模式,首先创建两个相同的变量,然后对它们作比较
![](https://i-blog.csdnimg.cn/blog_migrate/6420bc078f8d7bcf7ae148fe6364c67e.png)
两个字符串的值是相同的,经过这三种方法的比较之后,输出的都是True结果。
前两个比较是比较值,没什么问题,那么比较引用为什么也输出True呢,我们知道,内存在分配引用类型的时候,实际给的是一个引用地址,
这里明明有两个引用地址才对,为什么会输出相同呢?其实,这也就是CLR在给赋值的时候使用了享元模式
它也有一个静态字典,在每次赋值时就判断有没有这个值。如果有就取得相同的引用地址给它。
另外还有种特殊的情况,就是使用string.Format
![](https://i-blog.csdnimg.cn/blog_migrate/622a9f356eb8da73e5908fc57c83c267.png)
这样输出的结果是True,True,Flase
从这里可以看出string.Form在申请内存的时候,只是申请了Big,然后后面留了个空内存,等待动态运行时改变了这个变量
所有它和A指向的不是一个引用地址