---
# System prepended metadata

title: Pattern - Singleton
tags: [Code Sense, Design-Pattern]

---

###### 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");
    }
}
```