一,什麼是享元模式?
享元模式(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來實作唯一性
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,享元模式的原理,字元串享元是全局的,不是局部的,在一個程序中,隻有一個堆,是以指向同一個位址的字元串是一樣的