通俗易懂設計模式解析——享元模式
- 2019 年 10 月 3 日
- 筆記
前言
今天我們繼續講述設計模式,今天提及的是享元模式,享——共享。之前不是出現了一系列共享的東西嗎?為啥呀,還不就是有些東西每個人都需要,但是每個人都去買一個又有點浪費。所以出現共享。話費一定的經濟可以使用,使用完成之後又歸還。這就是享。分享共享。今天講的享元模式跟這相類似。享元模式——通俗來說也就是共享最小單元的一種模式。我們就一起看看到底啥是這享元模式吧。
享元模式介紹
一、來由
在軟件系統開發中,我們肯定會出現重複使用同一個對象的情況,每次使用都new一個對象出來。這樣的話對於內存來說就需要多次反覆的申請內存了。這樣使用內存也就越來越多,這會出現大問題的。那麼能不能創建new一個對象。然後使用的時候就共同使用一個就好了。這也就是享元模式的含義所在了——共享一個對象。
二、 意圖
運用共享技術有效地支持大量細粒度的對象。
三、 案例圖
四、享元模式代碼示例
看上面的案例圖我們可以發現享元模式主要包含以下部分:
享元工廠角色:這個角色主要負責創建和管理享元角色。判斷是否存在符合要求的享元對象,如果存在則直接拿取,如果不存在的話就會創建一個享元對象並保存。
抽象享元角色:這個角色是所有享元角色的基類。提供需要實現的公共接口
具體享元角色:繼承於抽象享元角色。實現其抽象的接口。
客戶端:負責調用並處理邏輯,且保存多有享元對象的狀態
在我們平時使用的編輯器中,會出現很多的字,這些字也是會一直重複出現的。那麼我們現在就試着使用享元模式來對這些字進行處理。這裡暫且使用字母代替:
namespace Flyweight_Pattern { class FlyweightPattern { } #region 抽象享元角色——抽象公共接口 public abstract class Flyweight { /// <summary> /// 表述輸出的位置 /// </summary> /// <param name="externalstate">外在的狀態,隨着環境改變而改變</param> public abstract void OutInput(int externalstate); } #endregion #region 具體享元角色——實現其具體 public class SpecificFlyweight : Flyweight { private string Innerstate; /// <summary> /// 內部狀態接收 /// </summary> /// <param name="innerstate">內部狀態</param> public SpecificFlyweight(string innerstate) { this.Innerstate = innerstate; } /// <summary> /// 實現方法 /// </summary> /// <param name="externalstate">外部狀態</param> public override void OutInput(int externalstate) { Console.WriteLine($"內部狀態:{Innerstate}————外部狀態:{externalstate}"); } } #endregion #region 享元工廠角色——對享元角色進行創建及管理的 public class FlyweightFactory { public Dictionary<string, SpecificFlyweight> keyValuePairs = new Dictionary<string, SpecificFlyweight>(); public SpecificFlyweight GetFlyweight(string Key) { SpecificFlyweight specific = null; if (!keyValuePairs.ContainsKey("A")) { Console.WriteLine("暫時未遇見該Key"); keyValuePairs.Add(Key, new SpecificFlyweight(Key)); Console.WriteLine("現已保存該Key"); } else { specific = keyValuePairs[Key] as SpecificFlyweight; } return specific; } } #endregion }
namespace Flyweight_Pattern { class Program { static void Main(string[] args) { ///初始化享元工廠 FlyweightFactory factory = new FlyweightFactory(); while (true) { Console.WriteLine("請輸入字符的位置:"); var indexstring = Console.ReadLine(); if (int.TryParse(indexstring, out int index)) { Console.WriteLine("請輸入字符:"); string str = Console.ReadLine(); ///判斷字符是否創建 Flyweight flyweight = factory.GetFlyweight(str); //如果存在則輸出信息 if (flyweight != null) { flyweight.OutInput(index); } } else { Console.WriteLine("請輸入數字!"); } Console.WriteLine("結束請輸入N"); if (Console.ReadLine()=="N") { break; } } Console.WriteLine("已結束"); Console.ReadLine(); } } }
使用場景及優缺點
這裡我們需要注意的是劃分好外部狀態和內部狀態,否則混淆之後可能引起線程安全問題。同時必不可少的是一個工廠對象進行控制。
這裡我們解釋下在享元模式中的內在狀態和外在狀態:
內在狀態:不隨環境的變化而變化,上面例子中不管位置如何變化,A就是A。字母A就是內在狀態。
外在狀態:會隨着環境的變化而變化,上面例子中位置變化所以輸出的位置也是不一致的。字母A的位置就是外在狀態
一、 使用場景
對於享元模式來說其使用場景可分以下四點:
1、系統需要大量相似對象
2、創建這些對象需要花費大量資源
3、狀態可分為內在狀態和外在狀態,可以根據內在狀態分為各種組。
4、需要緩衝池的場景
二、 優點
1、享元模式的優點最主要的是極大的減少了系統中對象的創建,降低了內存使用提高了效率
三、 缺點
任何東西來說都是有利有弊的,我們看下享元模式又存在哪些弊端呢
1、為了使一些對象共享,對對象區分了內在狀態和外在狀態,使系統邏輯變為複雜了,使系統更加難於理解了。
擴展
我們再看看享元模式和之前我們有提到過的原型模式和單例模式的一些區別吧。我們首先回顧下前面所說的原型模式和單例模式:
原型模式:使用原型實例指定創建對象的種類,然後通過拷貝這些原型來創建新的對象。
單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
享元模式:運用共享技術有效地支持大量細粒度的對象。
原型模式——享元模式
我們再來分析,原型模式是通過原型實例指定創建對象。達到類的復用。通過深淺拷貝類來實現復用以節約資源開銷。再看享元模式抽出相似的一些對象,定義其內在狀態和外在狀態來達到對象共享。運用共享的技術來支持大量細粒度的對象。也就是前面更傾向於實現復用,而享元模式更加傾向於共享使用。所以也不適合放一起進行比較的。
單例模式——享元模式
我們再看單例模式,單例模式保證的是類僅有一個實例,然後提供一個全局訪問點,所有訪問都可以訪問得到。達到的是一個實例數據的共享。我們再看享元模式,享元模式通過的是將相似的對象進行共享,然後控制內在狀態和外在狀態來達到變化。享元模式是對象級別的,也就是實現的是多個對象——對象池。而單例模式是一類一個對象的實例。享元模式更加註重的是節約內存空間,提高其性能。然而單例模式注重的是數據的共享。
總結
今天的享元模式就暫時介紹的這麼多,享元模式精髓也就是達到對象的共享,達到共享的話就需要抽出一部分東西達到相似。所以這又區分了內在狀態和外在狀態。內在狀態不隨環境變化而變化,屬於可共享的。而外在狀態隨着環境改變而改變,所以不能共享的。在.Net開發中,我們經常使用到的額String類型的實現就採用了享元模式的設計。在string中具有相同字符序列的String對象不會重複創建。具體細節想要研究的可以自行查詢資料。在我們使用設計模式的時候有一些點還是需要多次強調及注意的。基於原則設計。視情況採用設計模式。不要為了使用設計模式而去使用設計模式。一切都是為了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
勇敢的面對不一定成功,但你不面對就一定不成功。
歡迎大家掃描下方二維碼,和我一起踏上設計模式的闖關之路吧!