> For the complete documentation index, see [llms.txt](https://www.saladpuk.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.saladpuk.com/beginner-1/design-patterns/creational/singleton-pattern.md).

# ☝️ Singleton Pattern

เจ้าตัวนี้ผมขอตั้งชื่อเป็นภาษาไทยว่า **หนึ่งเดียว** และมันอยู่ในกลุ่มของ 🤰 [**Creational Patterns**](https://saladpuk.gitbook.io/learn/beginner-1/design-patterns/creational) ซึ่งเจ้าตัวนี้จะมาช่วยแก้ปัญหาเมื่อเราต้องการจะสร้างคลาสพิเศษที่สามารถนำไปสร้างเป็น object ได้เพียงแค่ตัวเดียวเท่านั้น และ สามารถเข้าถึงได้จากตรงไหนก็ได้ ดังนั้นลองไปดูโจทย์ของเรากันเลยละกัน

{% hint style="info" %}
**แนะนำให้อ่าน**\
บทความนี้เป็นส่วนหนึ่งของมหากาพย์ Design Patterns ที่จะมาเป็น guideline ในการแก้ปัญหาในการออกแบบซอฟต์แวร์โปรเจค หากใครสนใจอยากเข้าใจตั้งแต่ต้นว่ามันคืออะไร และเจ้า patterns ทั้ง 23 ตัวมีอะไรบ้าง ก็สามารถจิ้มตรงนี้เพื่อไปอ่านบทความหลักได้เบยครัช [👦 **Design Patterns**](https://saladpuk.gitbook.io/learn/beginner-1/design-patterns)
{% endhint %}

{% hint style="warning" %}
**หมายเหตุ**\
เนื้อหาของบทความนี้จะเน้นให้เข้าใจหลักการทำงานของ Design Patterns แต่ละตัว โดยใช้เกม Ragnarok เป็นการอธิบาย ซึ่งบางอย่างอาจจะไม่ตรงกับตัวเกมจริงๆนะขอรับ *Gravity อย่ามาจับผมนะผมโดนแมวน้ำครอบงำ + รู้เท่าไม่ถึงการ + ผมเป็นคนดี + ผมมีลูกมีเมียมีสามีที่ต้องดูแล* 😭
{% endhint %}

## 🧐 โจทย์

สมมุติว่าในเกมของเรามีอีเว้นท์พิเศษตัวนึง โดยมันจะปล่อย Boss ที่ชื่อว่า **Detardeurus** มาให้ผู้เล่นทุกคนช่วยกันปราบ ซึ่งถ้ามีผู้เล่นคนไหนปราบมันลงได้ มันก็จะกลับมาเกิดใหม่ในทุกๆ 2 ชั่วโมง ตามรูปด้านล่าง

![](/files/-Lshl3MiITqSX15rrfzc)

และด้วยความพิเศษที่มันเป็นอีเว้นท์ เลยทำให้เจ้าบอสตัวนี้จะต้อง **มีได้เพียงตัวเดียวเท่านั้น** และ **ผู้เล่นทุกคนจะต้องเข้าถึงข้อมูลบอสตัวนี้ได้** ไม่ว่าจากที่ไหนก็ตามอีกด้วย

![ผู้เล่นทุกคนเข้ามาดูได้ว่าสุขภาพของบอสเป็นยังไง ใกล้ถึงเวลาเกิดหรือยัง](/files/-LtpXiyRyW8Xjgo0S_R1)

แล้วเราจะเขียนโค้ดออกมายังไงดี เพื่อให้มันสามารถตอบโจทย์ความต้องการแบบนี้ ได้อย่างไม่มีปัญหากันนะ ?

## 🧒 แก้โจทย์ครั้งที่ 1

จากที่ว่ามาก็น่าจะไม่มีอะไรยากชิมิ? ถ้าเราอยากให้บอสมันมีตัวเดียวเราก็แค่ไปสร้าง object ของบอสตัวนั้นขึ้นมา 1 ตัว แล้วเขียนเงื่อนไขลงไปว่าห้ามสร้างเกิน 1 ตัวนะจ๊ะ ตามโค้ดด้านล่างนี้ใช่ป่ะ

```csharp
public class EventBoss
{
    private Detardeurus boss = new Detardeurus();

    public Detardeurus GetDetardeurus()
    {
        return boss;
    }
}
```

ส่วนถ้าเราอยากให้ทุกคนสามารถเข้าถึง object ตัวนี้ได้จากที่ไหนก็ได้ เราก็จะให้มันเป็น **`static member`** ยังไงล่ะ แก้โค้ดแพร๊บ

```csharp
public class EventBoss
{
    private static Detardeurus boss = new Detardeurus();

    public static Detardeurus GetDetardeurus()
    {
        return boss;
    }
}
```

ไหนลองตรวจดูดิ๊ว่าตอนที่ไปเอา object นี้ออกมา มันจะเป็น object เดียวกันหรือเปล่านะ

```csharp
static void Main(string[] args)
{
    var boss1 = EventBoss.GetDetardeurus();
    var boss2 = EventBoss.GetDetardeurus();

    Console.WriteLine(boss1 == boss2);
}
```

> **ผลลัพท์**\
> True

ซึ่งดูเหมือนว่าเราจะแก้โจทย์นี้เสร็จแล้วใช่ไหม เพราะใครอยากได้ object ของบอสตัวนี้ก็แค่เรียกผ่านเมธอด **GetDetardeurus()** ก็จะได้ object ตัวเดียวกันไปใช้งาน แถมเมื่อมันเป็น static ใครจะมาเรียกใช้งานก็สามารถทำได้เลยนั่นเอง ตามรูปด้านล่าง

![](/files/-Lw4zoHWvtmX6S96NX6T)

เสียใจด้วยนะมันไม่ได้ง่ายแบบนั้นหรอก แม้ว่าเราจะได้ object เดียวกันกลับมาเสมอก็จริง แต่เราจะต้องเรียกใช้งานผ่าน **EventBoss.GetDetadeurus()** เท่านั้น ... แล้วมันจะเกิดอะไรขึ้นถ้ามีคนอื่นดันไปสร้าง object นั้นขึ้นมาตรงๆด้วยคำสั่ง `new` กันล่ะ ?

```csharp
var myboss1 = new Detardeurus();
var myboss2 = new Detardeurus();

Console.WriteLine(myboss1 == myboss2);
```

> **ผลลัพท์**\
> False

เพียงแค่โค้ดด้านบนก็จะทำให้มันมีบอสแบบนี้เกิดขึ้นมาใหม่ 2 object แล้วยังไงล่ะ ดังนั้นโค้ดด้านบนเลยไม่สามารถตอบโจทย์เราได้อย่างแท้จริงนั่นเอง

## 🧒 แก้โจทย์ครั้งที่ 2

### 🔥 วิเคราะห์ปัญหา

ถ้าเราวิเคราะห์ปัญหาดีๆ สาเหตุที่แท้จริงของปัญหาในตอนนี้คือ **ใครอยากสร้าง object นี้ก็สร้างได้เลย** เพียงแค่ใช้คำสั่ง **`new`** นั่นเอง (เพราะมันคือพื้นฐานของ class)

แต่ถ้าเราลองไล่ลำดับการทำงานของ class จริงๆเราจะพบว่า เมื่อใช้คำสั่ง new ปุ๊ป สิ่งแรกที่มันจะทำก็คือ มันจะเรียกใช้ **`Constructor`** เป็นลำดับแรก ซึ่งโดยปรกติ **default constructor จะเป็น public** นั่นเอง เลยทำให้ใครอยากสร้าง object ก็สามารถสร้างได้เลย ตามรูป

![](/files/-Lq6swpQUgaCcHXg_IjD)

{% hint style="success" %}
**แนะนำให้อ่าน**\
สำหรับใครที่ลืมหรืออยากทบทวนการทำงานของ Class & Constructor ก็สามารถเข้าไปดูได้จากลิงค์ตัวนี้เลยครัช [**มารู้จักกับ Constructor กันบ้าง**](https://saladpuk.gitbook.io/learn/beginner-1/csharp101/intermediate/constructor)
{% endhint %}

ดังนั้นเพื่อแก้ปัญหาไม่ให้คนอื่นมาสร้าง object ได้เองมั่วซั่ว เราก็จะทำการ **เปลี่ยน Constructor ให้เป็น private** ซะ เพียงเท่านี้เราก็จะไม่สามารถใช้คำสั่ง new ในการสร้าง object จากคลาสนี้ได้แล้วนั่นเอง ตามรูปเบย

![](/files/-LpKAL4i7lDLHTJNs4zZ)

```csharp
public class Detardeurus
{
    private Detardeurus()
    {
    }
}
```

ไม่เชื่อลองไปเขียนโค้ดดูดิ มันจะสร้าง object จากคลาสนั้นไม่ได้เลย

```csharp
new Detardeurus(); // ERROR
```

เพียงแค่นี้ก็ไม่มีคนสร้าง object จากคลาสพิเศษของเราได้ละ ... แต่ก็เกิดคำถามใหม่ว่า ถ้าใช้คำสั่ง new **สร้าง object ไม่ได้ แล้วเราจะเอา object ของมันออกมาได้ยังไงกันล่ะ ?**

### 🔥 แก้ไขปัญหา

เมื่อเราคิดต่อดูอีกที การที่เราเปลี่ยน constructor เป็น **private มันจะทำให้ภายนอกไม่สามารถเข้าถึงได้** แต่ว่า **ภายในยังสามารถเข้าถึงได้ตามปรกติ** นั่นเอง

ดังนั้นเราก็จะสร้าง object มันจากภายในคลาสมันเองยังไงล่ะ!! ตามรูปเลย อะชึบอะชึบ

```csharp
public class Detardeurus
{
    private Detardeurus instance;

    private Detardeurus()
    {
        instance = new Detardeurus();
    }
}
```

เห็นไหมว่าภายในก็ยังใช้งาน constructor ตัวเองได้ตามปรกติ ดังนั้นก็จะเหลือแค่ **จะให้ภายนอกเข้ามาใช้งาน object ยังไงนั่นเอง** ... ซึ่งตรงนี้มันก็ไม่ยากอีกต่อไป เพราะในโค้ดแรกสุดที่เราออกแบบไว้นั้นทำไว้เรียบร้อยแล้ว โดยการเปิดให้ภายนอกเข้าถึงได้ผ่าน static member นั่นเอง ดังนั้นเราก็จะได้โค้ดออกมาเป็นแบบนี้

```csharp
public class Detardeurus
{
    private static Detardeurus instance;

    private Detardeurus()
    {
        instance = new Detardeurus();
    }

    public static Detardeurus GetInstance()
    {
        return instance;
    }
}
```

จบเรียบร้อยแล้ว เพียงเท่านี้เราก็จะได้คลาสพิเศษที่ทั้งโปรแกรมของเรามี object ได้เพียง 1 ตัวเท่านั้น (เพราะภายนอกมันสร้าง object ตัวนี้ไม่ได้) แถมยังมีช่องทางให้เข้าถึงได้จากทุกที่อีกด้วย (ผ่านทาง static นั่นเอง)

![](/files/-LpOzU31AUxJfwlFwDxv)

## 🤔 Singleton คือไย ?

### 🔥 จุดกำเหนิด

ในบางครั้งเราต้องการให้ object ถูกจำกัดจำนวนครั้งที่สร้างได้ และ ต้องการให้มีช่องทางที่เข้าถึง object เหล่านั้นได้ง่ายๆ

### 🔥 ผลจากการใช้

เราสามารถจำกัดการสร้าง object ได้ตามที่เราต้องการ และมีช่องทางเข้าถึงแบบเจ้า object พวกนั้นแบบ Global

### 🔥 วิธีการใช้

ถ้าเราอยากให้คลาสไหนถูกจำกัดจำนวนในการสร้าง object เราก็แค่ ห้ามให้คนอื่นสร้าง object ได้ตามใจด้วยการทำให้คลาสนั้นเป็น **private constructor** ซะ ส่วนเงื่อนไขและการสร้าง object ตัวนั้นก็จะถูกจัดการอยู่ภายในคลาสตัวนั้นเอง และเปิดช่องทางให้คนอื่นเข้าถึงผ่าน **static member** ... เพียงแค่นี้เราก็จะได้คลาสที่เป็น **Singleton** เรียบร้อยแบ้ว

![](/files/-M-V6K6POmlMRORYF0e1)

ไหนลองเอาที่เราออกแบบมาเทียบกันดูดิ๊ ... เหมือนกันเปี๊ยบเบย

![](/files/-LpsQRWEj24IcAKwErhS)

## 🤠 เทคนิค

วิธีการนำ Singleton ไปใช้นั้นมีหลายแบบเลย ซึ่งแต่ละแบบก็จะมีข้อดีข้อเสียที่ต่างกันด้วย ดังนั้นเราลองมาดูกันหน่อยว่ามันมีอะไรกันบ้าง

### 🔥 Lazy Initialization

เป็นหลักในการ **สร้าง object เมื่อมันจำเป็นค่อยสร้าง** ดังนั้นโดยปรกติ object ของเราจะไม่ถูกสร้างขึ้นมาจนกว่าจะมีคนต้องการแล้วเรียกใช้ มันถึงจะเริ่มทำการสร้าง object นั้นอีกที ซึ่งเราสามารถเขียนโค้ดออกมาได้แบบนี้

```csharp
public class Singleton
{
    private static Singleton instance;

    private Singleton() { }

    public static Singleton GetInstance()
    {
        if (instance == null)
        {
            instance = new Singleton();
        }
        return instance;
    }
}
```

ซึ่งจากโค้ดจะเห็นว่า ถ้าไม่เคยมีใครเรียกใช้เมธอด **GetInstance()** นั่นก็หมายความว่า object ตัวนี้ก็จะไม่เคยถูกสร้างเลยนั่นเอง และ ถ้ามันเคยถูกสร้างแล้ว มันก็จะไม่ต้องไปสร้างใหม่อีกเลย

#### 👍 ข้อดี

* ไม่เสียเวลาในการสร้าง
* ไม่เปลือง memory

#### 👎 ข้อเสีย

* ตอนจะสร้างถ้ามันต้องไปทำงานนั่นนู่นนี่เยอะ มันจะทำให้โปรแกรมดูหน่วงๆหน่อยนึง จนกว่าจะสร้างเสร็จ
* มีปัญหากับการทำงานแบบ Multi-Threading เพราะมันมีโอกาสเข้าไปสร้าง instance พร้อมกัน

### 🔥 Early Initialization

เป็นด้านตรงกันข้ามกับ Lazy Initialization เพราะมันจะสร้าง object ทิ้งไว้เลยตั้งแต่แรก ซึ่งเป็นโค้ดตามตัวอย่างแรกๆที่เราเขียนไว้ด้านบนเลย

```csharp
public class Singleton
{
    private static Singleton instance = new Singleton();

    public static Singleton GetInstance()
    {
        return instance;
    }

    private Singleton() { }
}
```

#### 👍 ข้อดี

* หลังจากที่มันสร้างเสร็จมันจะพร้อมใช้งานทันที ดังนั้นตอนที่ถูกเรียกใช้ มันจะไม่รู้สึกหน่วงๆ
* ไม่มีปัญหากับ Multi-Threading

#### 👎 ข้อเสีย

* เสียเวลาในการสร้าง (ไปหน่วงตอนเปิดแอพแทน)
* เปลือง memory เพราะ object นั้นอาจไม่เคยถูกเรียกใช้เลยก็ได้ แต่มันถูกสร้างไว้แล้ว

### 🔥 Bindable Object

โดยปรกติเวลาที่เราทำงานร่วมกับ object ที่มีการเรียกเอาไปใช้งานนั้น เราจะไม่ค่อยสร้างเป็นเมธอดสักเท่าไหร่ เพราะมันเอาไปใช้ในการทำ **Data Binding** ไม่ได้ (เช่นพวก MVC, MVVM) ดังนั้นเพื่อเป็นการแก้ปัญหาเราจะนิยมไปสร้างเป็น **Property** มากกว่านั่นเอง

```csharp
public class Singleton
{
    private static Singleton instance;

    public static Singleton Instance
        => instance;

    protected Singleton()
    {
        instance = new Singleton();
    }
}
```

#### 👍 ข้อดี

* นำไปใช้ในการ Binding ได้เลย (1-2 ways ได้หมด)

#### 👎 ข้อเสีย

* บางภาษาอาจจะไม่เหมาะสมทางเทคนิค

## 🥴 ข้อผิดพลาดที่เจอบ่อยๆ

### ⛔ ใช้ static class แทน

การใช้ static class แทนการทำ Singleton pattern นั้นจริงๆก็สามารถทำได้นะ **ถ้าเราสามารถคุมการทำงานมันได้** แต่มันจะง่ายกว่าไหมเพียงแค่เปลี่ยนมันเป็น Singleton Pattern แล้วดูแลมันเป็น object ธรรมดาไปเลย ?

### ⛔ ไม่ใช้ private constructor

ถ้ามาคิดถึง Access Modifier จริงๆแล้วก็มีอีกหลายตัวนะที่ใช้แทน private ได้ เช่น **protected** (internal ยังไม่สมควรเพราะมันถูกสร้างได้จาก internal namespace นั่นเอง) แต่ถามว่ามันสมควรใช้ของพวกนั้นแทน private ไหม คำตอบคือไม่สมควร เพราะเรามีความตั้งใจที่อยากจะให้มันถูกควบคุมดูแลได้จากที่เดียวอยู่แล้ว ดังนั้นมันไม่ควรมีที่ไหนเข้ามาแก้ไขได้นอกจากตัวเองอีก + การทำ sub class จาก singleton จะมีปัญหาอื่นๆตามมาอีก

## 🎯 บทสรุป

### 👍 ข้อดี

* ช่วยให้เราสามารถควบคุมการสร้าง object ได้
* มีช่องทางให้เข้าถึงแบบ Global
* ซ่อนความวุ่นวายในการสร้าง object
* ถ้าการสร้าง object มีการเปลี่ยนแปลง ก็สามารถแก้ได้จากจุดเดียว

### 👎 ข้อเสีย

* ยากต่อการจัดการกับ Life cycle ของมัน
* มีปัญหากับการเขียนเทส
* มีปัญหากับ Multi-Threading ถ้าไม่จัดการให้ดี

### 🤙 ทางเลือก

เราสามารถนำ Framework พวก **Dependency Injection (DI)** เข้ามาใช้แทนได้นะจ๊ะ โค้ดกระชับหลับสบายเต็มตื่นด้วย

{% hint style="danger" %}
**ข้อควรระวัง**\
**อย่านำ Singleton Pattern ไปใช้มั่วซั่ว** เพราะมันทำให้โค้ดของเราซับซ้อนขึ้นเยอะเลยแทนที่เราจะใช้คำสั่ง **new** แบบปรกติ ดังนั้นให้ชั่งน้ำหนักให้ดีเสียก่อนว่าปัญหาที่เราเจออยู่นั้น มันวุ่นวาย เทสยาก โค้ดมันผูกกันอยู่เยอะหรือเปล่า ถ้าชั่งน้ำหนักแล้ว + มีเหตุผลที่เพียงพอที่จะใช้ก็จงใช้ให้สบายใจไปเถิด
{% endhint %}

{% hint style="success" %}
เกลียด ชอบ ถูกใจ อยากติดตาม อยากติชมแนะนำด่าทอ หรืออะไรก็แล้วแต่ (ห้ามมายืมเงิน) จิ้มลงมาที่เพจนี้ได้เลย [**Mr.Saladpuk**](https://www.facebook.com/mr.saladpuk) และจะเป็นประคุณอันล้นพ้นถ้ากด Like + Follow + Share ให้ด้วยขอรับ น้ำตาจิไหล 🥺
{% endhint %}
