> 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/factory-method-pattern.md).

# Factory Method

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

{% 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 อย่ามาจับผมนะผมโดนแมวน้ำครอบงำ + รู้เท่าไม่ถึงการ + ผมเป็นคนดี + ผมมีลูกมีเมียมีสามีที่ต้องดูแล* 😭

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

## 🧐 โจทย์

สมมุติว่าเราเขียนเกมที่มีแผนที่ 2 แบบคือแบบ **ป่า (Payon)** และ **ทะเลทราย (Desert)** ตามรูป

![](/files/-LnS9fW1XQaZoTPmKeTg)

ซึ่งภายในแผนที่นั้นจะมีตัว **Slime** น้อยน่ารักเป็น monster เดินอยู่ภายในแผนที่เหล่านั้น แต่ความวุ่นวายของมันก็คือ

* Slime ที่อยู่ใน **Payon** นั้นจะเป็นตัวสีเขียว ชื่อว่า **Poporing** ที่มีความสามารถในการ**ปล่อยพิษได้**
* Slime ที่อยู่ใน **Desert** จะเป็นตัวสีเหลือง ชื่อว่า **Drops** ที่สามารถ**ปล่อยไฟได้**
* แผนที่ **Payon** ถ้าเป็นเวลา **18:00** ตรงเราจะเจอบอส Slime ที่ชื่อว่า **Angeling**
* แผนที่ **Desert** ถ้าเป็นเวลา **19:00** ตรงเราจะเจอบอส Slime ที่ชื่อว่า **Ghostring**
* และแผนที่ทั้ง 2 ถ้าเป็นเวลา **12:00** เราจะได้ Slime ที่ชื่อว่า Poring ซึ่งถ้ามันเกิดในแผนที่ **Payon มันจะปล่อยพิษได้** แต่ถ้ามันเกิดในแผนที่ **Desert มันจะปล่อยไฟได้**

ซึ่งทั้งหมดที่ว่ามาก็จะสรุปเป็นภาพคร่าวๆได้ประมาณนี้

![](/files/-LsZazFdybDMm6FIEyLk)

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

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

จากที่อ่านมาก็ดูเหมือนมันจะไม่ยากอะไรเลยชิมิ? เจ้าพวก Slime นั้นก็น่าจะเป็นกลุ่มตระกูลเดียวกันดังนั้นก็น่าจะอยู่ในรูปนี้ได้

![](/files/-Lus_apuAs9S9IlAy0VD)

ส่วนตอนสร้าง Slime ก็แค่สร้างเงื่อนไขว่า **ตอนนี้อยู่แผนที่อะไร** และ **เป็นเวลาเท่าไหร่** เพียงเท่านี้เราก็จะสามารถสร้าง Slime ได้ถูกต้องตามเงื่อนไขที่ว่ามาละ ตามโค้ดด้านล่างเบย (ขอเขียนเวลาเป็น string นะจะได้เข้าใจง่ายๆหน่อย)

```csharp
public Slime CreateNewSlime(string mapName, string currentTime)
{
    if(mapName == "Payon")
    {
        if(currentTime == "12:00")
        {
            return new Poring("Poison");
        }
        else if(currentTime == "18:00")
        {
            return new Angeling();
        }
        else
        {
            return new Poporing();
        }
    }
    else
    {
        // โค้ดคล้ายๆด้านบนแหละลองคิดเล่นๆดู
    }
}
```

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

{% hint style="danger" %}
**Open & Close Principle (OCP)**\
อันนี้เป็นตัวอย่างในการออกแบบที่**ละเมิดหลักในการออกแบบที่ชื่อว่า OCP** นั่นเอง ซึ่งมันทำให้ทุกครั้งที่มีของใหม่ๆถูกเพิ่มเข้าไปปุ๊ป เราก็ต้องไปแก้โค้ดเดิมเสมอ สังเกตุได้ว่าถ้ามี Slime แบบใหม่เข้ามา หรือมีแผนที่แบบอื่นๆอีก เราก็จะต้องไปไล่แก้เจ้าพวก IF-ELSE ที่อยู่ด้านบนกันใหม่ทุกครั้งนั่นเอง
{% endhint %}

{% hint style="success" %}
**แนะนำให้อ่าน**\
สำหรับใครที่ลืมหลักในการออกแบบเรื่องนี้ไปแล้วให้กดอ่านได้จากตรงนี้ [**Open & Close Principle**](https://saladpuk.gitbook.io/learn/basic/solid/ocp)
{% endhint %}

ถ้าใครคิดตามแล้วได้คำตอบออกมาเหมือนในโค้ดด้านบน ก็ไม่ต้องตกใจอะไรนะ เพราะจริงๆเจ้าโค้ดด้านบนนั้นมันก็คือ **โรงงานผลิตอย่างง่าย** ของ pattern ตัวนี้ ซึ่งเรามักเรียกมันว่า **Simple Factory** นั่นเอง โดยเจ้ารูปแบบนี้มันจะยังไม่สมบูรณ์เพราะมันยังขัดกับหลักการออกแบบหลายๆอย่างอยู่นั่นเอง

ก่อนที่จะไปทำให้มันสมบูรณ์ ผมจะขอแยกเจ้าเมธอด **CreateNewSlime()** ออกมาเป็น Model ที่ใช้ในการผลิต Slime ก่อนดีกว่า ซึ่งภาพด้านล่างคือโครงสร้างของ **`Simple Factory`** นั่นเอง

![Simple Factory หรือ รูปแบบอย่างง่ายของ Factory Pattern ที่ยังไม่สมบูรณ์นั่นเอง](/files/-LnNWoTwysG98bbnIcnT)

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

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

จากปัญหาที่ว่ามาเราจะพบว่า ทุกครั้งที่มีเงื่อนไขใหม่ มี Slime แบบใหม่ มีแผนที่แบบใหม่ หรืออะไรก็ตาม มันจะทำให้ เราต้องไปแก้เจ้าคลาส SimpleSlimeFactory เสมอ เลยทำให้ในอนาคตมันจะ **บวมฉ่ำ** อย่างไม่ต้องสงสัยเลย

![SimpleSlimeFactory จะบวมขึ้นไปเรื่อยๆถ้า Slime เพิ่มขึ้น หรือ แผนที่เพิ่มขึ้น](/files/-LoIJrBKCYgokD3Vr8-U)

ส่วนสาเหตุที่มันบวมก็เพราะว่า **มันดูแลเรื่องจุกจิกจากหลายเรื่อง** เช่น ต้องคอยรับแผนที่ใหม่ๆ ต้องคอยรับ Slime แบบใหม่ๆ และไหนจะเงื่อนไขจุกจิกอย่างเวลาในแต่ละแผนที่อีกนั่นเอง

ดังนั้นเพื่อที่จะแก้ปัญหาการบวมฉ่ำของมัน เราก็ควรที่จะแยกเรื่องจุกจิกออกเป็นเรื่องๆ โดยถ้ามองจากรูปด้านบนเราก็จะสามารถแบ่งออกมาได้เป็น 2 กลุ่มคือ **แผนที่** กับ **Slime** นั่นเอง

![](/files/-Ln1FlgjohZbdBUFuYXX)

และถ้าเราดูความสัมพันธ์ของเจ้า 2 อย่างนี้ดีๆเราจะพบว่า **แผนที่เป็นตัวกำหนดว่าจะสร้าง Slime แบบไหน** นั่นเอง และรวมถึง**ความสามารถของ Slime** ด้วย ตามรูปด้านล่างเบย

![](/files/-Lw9B7b8yPDmhBdoCTBk)

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

จากที่ร่ายมาทั้งหมดเราจะสามารถหา **พฤติกรรม + หน้าที่การรับผิดชอบ** ออกมาได้ว่า

* **แผนที่** มีหน้าที่รับผิดชอบในการ **สร้าง Slime**
* **แผนที่** ต้องเป็นคน **ดูแลเงื่อนไขต่างๆ** ในการสร้าง Slime (เช่น เวลา, ความสามารถพิเศษ)
* **Slime** เป็นแค่ **ผลลัพท์** ที่เราอยากได้เพียงแค่นั้น

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

![](/files/-Lu0pkQqKqoPMbbb-l_v)

ซึ่งแต่ละโรงงานก็จะมีหน้าที่คอยดูแลการผลิต Slime ของใครของมันเอง ซึ่งตรงตามหลัก **SRP** อีกด้วย

{% hint style="success" %}
**Single-Responsibility Principle (SRP)**\
เจ้าสิ่งนั้นๆควรมีหน้าที่รับผิดชอบเพียงอย่างเดียว ซึ่งเจ้าโรงงานแต่ละโรงงานด้านบน มันก็จะดูแลแค่หน้าที่การผลิต Slime เพียงอย่างเดียวเท่านั้น ซึ่งถ้าเงื่อนไขในการผลิต Slime ของโรงงาน Desert เปลี่ยนก็ไม่ไปกระทบอะไรกับโรงงาน Payon นั่นเอง ส่วนใครที่ลืมหรืออยากทบทวนเรื่อง SRP สามารถเข้าไปอ่านได้จากลิงค์นี้เบย [**Single-Responsibility Principle**](https://saladpuk.gitbook.io/learn/basic/solid/srp)
{% endhint %}

แต่มันยังไม่จบเท่านั้น เพราะมันยังขัดกับ **OCP** (ปัญหาตัวแรกที่อยู่ด้านบน) อยู่เหมือนเดิม เพราะเวลามีอะไรใหม่ๆเข้ามา เราก็ต้องไปไล่แก้โค้ดเก่าของเราอยู่ดี ... แต่ถ้าเรามาคิดดีๆ เราก็จะพบว่า Payon และ Desert นั้นมันก็ **เป็นโรงงานทั้งคู่** อยู่แล้ว ดังนั้นมันก็น่าจะรวมร่างเป็นแบบนี้ได้นะ

![](/files/-Lmz3prehWD7WytdN6rN)

แต่จากรูปด้านบนจะเห็นว่าเมธอด **CreateNewSlime()** ที่อยู่ใน Base class (เส้นสีแดง) นั้น มันไม่รู้ว่าจะต้องสร้าง Slime จริงๆยังไง เพราะ Sub class ของมันต่างหาก (เส้นสีเขียว) คือตัวที่รู้ว่าในสถานะการณ์นี้จะต้องสร้าง Slime อะไรออกมา ดังนั้น Base class ควรจะปล่อยให้ Sub class ทำหน้าที่ในการสร้าง Slime ไปซะ เลยทำให้เราต้องเพิ่ม **Abstract method** เข้ามาอีกตัวให้ sub class รับผิดชอบเรื่องการสร้าง slime นั่นเอง

![](/files/-Lo3yXunuEkhYX2I_dw9)

{% tabs %}
{% tab title="SlimeFactory" %}

```csharp
public abstract class SlimeFactory
{
    public Slime CreateNewSlime(string currentTime)
    {
        return BuildASlime(currentTime);
    }

    protected abstract Slime BuildASlime(string currentTime);
}
```

{% endtab %}

{% tab title="PayonSlimeFactory" %}

```csharp
public class PayonSlimeFactory : SlimeFactory
{
    public override Slime BuildASlime(string currentTime)
    {
        if(currentTime == "12:00")
        {
            return new Poring("Poison");
        }
        else if(currentTime == "18:00")
        {
            return new Angeling();
        }
        else
        {
            return new Poporing();
        }
    }
}
```

{% endtab %}

{% tab title="DesertSlimeFactory" %}

```csharp
public class DesertSlimeFactory : SlimeFactory
{
    public override Slime BuildASlime(string currentTime)
    {
        // โค้ดก็คล้ายๆกับ PayonSlimeFactory แหละลองไปคิดเล่นๆดู
    }
}
```

{% endtab %}
{% endtabs %}

เพียงเท่านี้เราก็สามารถแก้ปัญหาโจทย์นี้ได้เรียบร้อยแล้ว แถมเมื่อเราทำงานกับโรงงานพวกนี้ เรายังสามารถใช้ **Best Practice** ในเรื่องของ [**Program to an interface and not to an implementation.**](https://saladpuk.gitbook.io/learn/beginner-1/design-patterns/creational) ได้อีกด้วย เพราะเราไม่ได้ทำงานกับระดับ Implementation แล้วยังไงล่ะ ซึ่งมันก็เป็นการเข้าข่ายกับ DIP ไปด้วยในตัวนั่นเอง เย่ๆ

{% hint style="success" %}
**Dependency-Inversion Principle (DIP)**\
เป็นหนึ่งในหัวใจของการออกแบบให้เราไม่ไปผูกการทำงานไว้กับ Low level module นั่นเอง ส่วนใครที่อยากศึกษาเรื่องนี้เพิ่มเติมก็สามารถกดไปอ่านได้จากลิงค์นี้เบย [**Dependency-Inversion Principle**](https://saladpuk.gitbook.io/learn/basic/solid/dip)
{% endhint %}

ยินดีด้วยในตอนนี้คุณได้ใช้สิ่งที่เรียกว่า **Factory Method Pattern** เรียบร้อยแล้ว ไม่ว่าจะรู้ตัวหรือไม่ก็ตาม เย่ๆ 👏

## 🤔 Factory Method คือไย ?

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

ในบางทีการสร้าง object นั้นมันก็ไม่ได้ง่ายเลย เช่น constructor มันรับหลายๆ parameters ก็ปวดหัวละ หรือถ้ามีการสร้าง object ที่สร้างยากๆตัวเดียวกันหลายๆจุดขึ้นมา มันก็แสดงว่าเราก็จะมีโค้ดแบบเดียวกันอยู่ซ้ำๆหลายที่เต็มไปหมด และการที่เราไปสร้าง object เองในบางทีก็อาจทำให้ [**Abstraction**](https://saladpuk.gitbook.io/learn/beginner-1/oop/abstraction) + [**Encapsulation**](https://saladpuk.gitbook.io/learn/beginner-1/oop/encapsulation) ที่วางไว้เสียหายโดยไม่ได้ตั้งใจก็เป็นได้

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

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

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

ตรงจุดนี้จะขออธิบายออกเป็นทีละขั้นตอนแบบนี้ละกัน คนที่พึ่งหัดออกแบบจะได้เข้าใจได้ง่ายๆนะ

object อะไรก็ตามที่เราอยากจะสร้าง เราจะเรียกมันว่า **Product** ซึ่งโดยปรกติเราก็จะมี product หลายๆแบบ เลยต้องทำมันเป็น **Interface** เอาไว้ (ตรงจุดนี้จริงๆจะเป็นอะไรก็ได้นะ ขอแค่เป็น abstraction ก็พอ)

![](/files/-LnNJbYPSjjryG6Spo7Q)

ส่วนคลาส Product ที่แท้จริงนั้นก็จะไป implement IProduct อีกที ซึ่งโดยปรกติเราจะเรียกคลาสที่แท้จริงเหล่านั้นว่า **Concrete class** นั่นเอง ดังนั้นในกรณีนี้ผมจะเรียกมันว่า *ConcreateProduct* ละกัน

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

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

![](/files/-LnIKBHWepqIWSQ3nYtq)

ดังนั้นสุดท้าย Factory ที่แท้จริง หรือ **ConcreteFactory** ก็จะเป็นคนมากำหนดว่าจะสร้าง Product ที่แท้จริงอะไรนั่นเอง

![](/files/-LshXre-aiAU19e121qx)

จากที่ร่ายยาวมาทั้งหมดเราก็จะได้แนวทางในการออกแบบที่ชื่อว่า **Factory Method Pattern** ออกมา ซึ่งมีหน้าตาประมาณรูปด้านล่างนี้นั่นเองครัช

![](/files/-LrKK_Xiig0BtTcMw_tS)

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

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

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

## 🤔 ทำไมต้องใช้ด้วย ?

ข้อดีที่สุดของการนำ Factory Method Pattern มาใช้ก็คือ **`มันลดการผูกกันของโค้ด (decoupling)`** นั่นเอง เพราะถ้าเราเขียนสร้าง object โดยใช้คำสั่ง `new` แบบเดิม มันจะมีปัญหาหลายๆอย่างตามมา ซึ่งผมขอยกตัวอย่างให้เห็นภาพเป็นตามด้านล่างละกัน

### 😱 Hardcode

สมมุติว่าเราเขียนโค้ดเป็นแบบด้านล่างนี้ละกัน

```csharp
var animal = new Dog();
```

ซึ่งดูแล้วก็เหมือนจะไม่ผิดบาปอะไรใช่ไหม แต่จริงๆมันหมายความว่า **โค้ดของเราต้องทำงานกับคลาส Dog เท่านั้น!!** ไม่มีวันเปลี่ยนเป็นอย่างอื่นเลย ถ้าอยากเปลี่ยนให้มันไปทำงานกับคลาส Cat แล้วล่ะก็ **เราก็ต้อง Build & Compile & Deploy ใหม่ทุกครั้งที่มีการแก้โดยเลี่ยงไม่ได้เลย**

### 😱 Dependencies

สมมุติว่าเราต้องไปสร้าง object ของคลาสด้านล่างนี้ โดยที่มันมี Constructor หน้าตาประมาณนี้

```csharp
public class DatabaseConnector
{
    public DatabaseConnector(IConnection conn,
        IResource resource,
        IProxy proxy,
        ICancelationToken cancelation,
        ..และอื่นๆ..)
    {
        // ทำอะไรซักอย่างไม่ต้องไปสนใจ
    }
}
```

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

### 😱 เทสยาก

สมมุติว่าโค้ดของเราเขียนเป็นแบบนี้ละกัน

```csharp
var dbConn = new MySqlConnector();
```

คำถามคือแล้วเวลาที่เราจะเทสมันล่ะ เราจะต้องยิงไปที่ตัว database จริงเพื่อเทสเลยเหรอ? หรือถ้าเราจะ Mock มันล่ะจะทำยังไง?

{% hint style="info" %}
เขียนจนหน้ากระดาษจะเต็มอยู่ละ เพียงเท่านี้ก็น่าจะเห็นข้อเสียที่ซ่อนอยู่กับการใช้คำสั่ง **`new`** ตรงๆกันแล้วนะว่ามันจะนำพาความสนุกอะไรมาให้เราบ้าง ลองไปคิดต่อเอาดูก็น่าจะได้อีกหลายเหตุผลเลยเพราะเจ้า **Coupling** ตัวเดียวนี่แหละ
{% endhint %}

## 🤔 ใช้แล้วดียังไง ?

อย่างที่บอกว่า Design Pattern ตัวนี้ มันจะทำให้โค้ดของเรา **`มันลดการผูกกันของโค้ด (decoupling)`** ดังนั้นปัญหาที่ว่าด้านบนมันจะละลายหายไปจนหมดเลย ตามนี้

### 👨‍🔧 ~~Hardcode~~

จากเดิมแทนที่เราจะใช้คำสั่ง `new` ไปสร้าง object ให้มันผูกติดกับคลาสนั้นๆเอง มันก็จะเปลายเป็นแบบนี้

```csharp
var animal = animalFactory.GetAnAnimal();
```

ซึ่งจากโค้ดใหม่นี้จะเห็นว่า ไม่มีการผูกติดกันอีกแล้ว มันจะสร้าง object อะไรมาให้เราก็ได้ ขึ้นกับความเหมาะสมของคลาสลูกนั่นเอง

### 👨‍🔧 ~~Dependencies~~

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

```csharp
var dbConn = dbConnectorFactory.CreateConnector();
```

### 👨‍🔧 ~~เทสยาก~~

เมื่อเราอยากจะเทสอะไรก็จะไม่ใช่เรื่องยากอีกต่อไป เพราะโค้ดของเราไม่ได้ผูกกับอะไรอีกแล้ว ดังนั้นเราก็จะสามารถ Mock เข้าไปตรงๆ หรือจะทำเป็น MockFactory เข้าไปก็ยังได้เลย ตามโค้ดด้านล่าง

```csharp
var dbConn = dbConnectorFactory.CreateConnector();

// เล่นตามเกมก็ได้
public class MockDbConnectorFactory : DbConnector { ... }

// หรือจะส่ง mock object เข้าไปแทนที่ผ่าน constructor ก็ได้
Constructor( mockFactory )
```

## 🎯 บทสรุป

### 👍 ข้อดี

การนำ Factory Method Pattern มาใช้งานนั้นจะช่วย **ลดการผูกกันของโค้ดลง** ทำให้เราสามารถเปลี่ยนแปลง แก้ไข รองรับสิ่งต่างๆได้มากขึ้น และมันยังช่วยเปิดให้เราทำพวก **Inversion of Control (IoC)** ได้ง่ายขึ้นด้วย

### 👎 ข้อเสีย

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

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

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

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

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