###### tags: `Code Sense` `Design-Pattern` # Pattern - Singleton ## 簡介 近期開始整理紀錄之前所用Pattern,Singleton為較常使用的Pattern中其中一種,屬於Creational Patterns創建型模式其中一種。 > Creational Patterns: Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code. Singleton物件在程式啟動後會自動在記憶體實體化一個區塊,像我們常見的Static Method。方法在不需要Create即可使用。 ## 使用情境與實作 - Static Method - Static Object Definition 這邊舉個例子,假如我們目前要實作某靜態方法,一般我們會用static撰寫,例如我們平常在系統中宣告一些共用設定變數(從Config檔讀取), ```=csharp //Static Method public class StaticAppSetting { public static readonly string AppName = GetConfigValue("AppName"); public static string GetConfigValue(string value) { return ConfigurationManager.AppSettings[value]; } } // Context class Program { static void Main(string[] args) { Console.WriteLine(StaticAppSetting.AppName); } } ``` static方法可讓我們很容易實現共用的靜態方法,但比較常在結構化程式被使用。如果我們要加入物件化概念,例如要做繼承以及使用介面,此時用純static method就無法滿足我們以物件設計為概念需求。 如果以Singleton實作,通常會做兩件事情, - 1.創建一個靜態私有Field(INSTANCE),並判斷是否Null去New Instance,另外建構子要設為不開放。 - 2.創建一個靜態方法或File可以獲取Instance 一般網路有不同寫法,目前朋友分享覺得寫得最漂亮的如下述 ```=csharp' //Singleton Pattern public class SingletonAppSetting { //Instance Method private static class SingletonHolder { static SingletonHolder() { } internal static readonly SingletonAppSetting INSTANCE = new SingletonAppSetting(); } // Instance Field public static SingletonAppSetting Instance { get { return SingletonHolder.INSTANCE; } } public readonly string AppName; // Construct protected SingletonAppSetting() { AppName = GetConfigValue("AppName"); } public string GetConfigValue(string value) { return ConfigurationManager.AppSettings[value]; } } // Context class Program { static void Main(string[] args) { Console.WriteLine(SingletonAppSetting.Instance.AppName); } } ``` 如果此時我們有另一個系統想沿用,有網路相關設置,用static的方式改寫,一個是新增新的static class,另一個則是修改原有static class如下 ```=csharp' public class StaticAppSetting { public static readonly string AppName = GetConfigValue("AppName"); public static readonly string LocalIp = GetConfigValue("LocalIp"); public static readonly string LocalPort = GetConfigValue("LocalPort"); public static string GetConfigValue(string value) { return ConfigurationManager.AppSettings[value]; } } ``` 如果用Singleton模式,則可宣告一個新的InternetAppSetting物件,去繼承原有AppSetting即可,如下 ```=csharp' public class SingletonInternetAppSetting : SingletonAppSetting { private static class SingletonHolder { static SingletonHolder() { } internal static readonly SingletonInternetAppSetting INSTANCE = new SingletonInternetAppSetting(); } public static new SingletonInternetAppSetting Instance { get { return SingletonHolder.INSTANCE; } } public readonly string LocalIp; public readonly string LocalPort; protected SingletonInternetAppSetting() { LocalIp = GetConfigValue("LocalIp"); LocalPort = GetConfigValue("LocalPort"); } } //Context class Program { static void Main(string[] args) { Console.WriteLine(SongletonInternetAppSetting.Instance.AppName); Console.WriteLine(SongletonInternetAppSetting.Instance.LocalIp); Console.WriteLine(SongletonInternetAppSetting.Instance.LocalPort); } } ``` 另外我們也可以使用介面來操作Singleton方法,例如我們宣告一個ISetting介面 ```=csharp public interface ISetting { string GetConfigValue(string value); } ``` 讓Object實作介面 > public class SingletonAppSetting : ISetting public class SingletonInternetAppSetting : ISetting 則在Context使用上則可直接抽換實作 ```=csharp class Program { static void Main(string[] args) { ISetting appSetting = SingletonAppSetting.Instance; var valueFromConfig = appSetting.GetConfigValue("Value"); appSetting = SongletonInternetAppSetting.Instance; var valueFromIni = appSetting.GetConfigValue("Value"); } } ```