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

# Abstract Factory

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

> 💡 ถ้าอยากเข้าใจ Abstract Factory Pattern ตัวนี้ได้เร็วขึ้น แนะนำให้อ่าน [🏭 **Factory Method Pattern**](https://saladpuk.gitbook.io/learn/beginner-1/design-patterns/creational/factory-method-pattern) ก่อนนะครัช (เพราะมันแทบจะเหมือนกันเลย)

{% 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/-Lw4zXvADjik0NKJyLzf)

ซึ่งภายในแต่ละแผนที่จะมี monster หลายๆแบบอยู่ในนั้น เช่น **สไลม์ (Slime)**, **หมาป่า (Wolf)**, **นกยักษ์ (Giant Bird)** แต่เนื่องจากสภาพแวดล้อมต่างกันเลยทำให้ monster ที่อยู่ในนั้นมี **หน้าตา กับ ชื่อเรียก ไม่เหมือนกัน** ตามรูปด้านล่าง

![](/files/-LnNEWLM3wJkhs0Ah9gL)

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

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

จากตรงนี้ผมอาจจะมองว่ามี monster อยู่ทั้งหมด 3 ประเภท ดังนั้นผมก็จะแบ่งมันออกเป็น Model ทั้งหมด 3 กลุ่มตามรูปด้านล่างเบย

![แบบนี้มันไม่ใช่การออกแบบที่ดีนะ แต่ขอเน้นไปที่เรื่อง Design Pattern ก่อน](/files/-LplDVVCNWVNmg9YmUw8)

ส่วนตอนเขียนโค้ดเราก็แค่ **สร้างเมธอดแยกตามประเภท monster** และก็ไปเขียนเงื่อนไขเอาว่า **ตอนนี้อยู่แผนที่อะไร** เพียงเท่านี้เราก็จะสามารถสร้าง monster ออกมาได้ตรงตามเงื่อนไขที่ว่ามาละ ตามโค้ดด้านล่างเบย

```csharp
public Slime CreateASlime(string mapName)
{
    if(mapName == "payon")
    {
        return new Poporing();
    }
    else
    {
        return new Drops();
    }
}

public Wolf CreateAWolf(string mapName)
{
    if(mapName == "payon")
    {
        return new WildWolf();
    }
    else
    {
        return new DesertWolf();
    }
}

// ที่เหลือไปคิดต่อเองคล้ายๆด้านบนแหละ
```

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

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

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

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

![](/files/-Lua92xLE4wdynTkuAZv)

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

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

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

![เวลาที่มีแผนที่ หรือ monster ใหม่ๆเข้ามา มันจะทำให้ MonsterFactory ใหญ่ขึ้นเรื่อยๆ](/files/-LnNYp1oqijB0TtEh_pS)

ส่วนสาเหตุการบวมนั้นเกิดจากเจ้า MonsterFactory ของเรามันดันไปดูแลทุกอย่างเลยยังไงล่ะ เช่น ดูแลเรื่องแผนที่ ดูแลเรื่องการสร้าง monster ซึ่งนี่คือหนึ่งในการละเมิดกฏของ SRP นั่นเอง

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

ดังนั้นเราจัดการเรื่อง **SRP** เสียก่อน โดยทำการแยกของที่อยู่ในนั้นออกมาเป็นเรื่องๆ ซึ่งในตัวอย่างของเราก็มีแค่ 2 เรื่องนั่นคือ **แผนที่** กับ **Monster** นั่นเอง

![](/files/-LoAMSHdVperDrLd1REx)

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

![](/files/-Lsf60aNq4O_ra0MPTIF)

แล้วถ้าเราดูเจ้าแผนที่ทั้ง 2 เราก็จะพบว่ามัน **สร้างของประเภทเดียวกัน แต่ต่างกันที่รายละเอียด** นั่นเอง เช่น แผนที่ต้องการสร้าง สไลม์, หมาป่า และ นกยักษ์ เหมือนกัน แต่ผลลัพท์จริงๆนั้นจะขึ้นอยู่กับแผนที่นั่นเอง ตามรูปด้านล่าง

![](/files/-LugZOnNCGjWh_eVlY9H)

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

จากที่ร่ายยาวมเราจะเริ่มมองเห็น **รูปแบบ** + **หน้าที่รับผิดชอบ** ของต่างๆตามนี้

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

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

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

ซึ่งเจ้าภาพด้านบน มันเป็นการสร้างกลุ่มของ monster ที่เป็นประเภทเดียวกันนั่นคือ

* **PayonMonsterFactory** จะสร้าง monster ที่อยู่ในป่า
* **DesertMonsterFactory** จะสร้าง monster ที่อยู่ในทะเลทราย

ดังนั้นถ้าเรามองจาก **พฤติกรรม** ของคลาสทั้ง 2 เราก็จะสามารถแยกมันออกมาเป็น **Interface** ที่ใช้ในการสร้าง Monster ได้ตามรูปด้านล่างนั่นเอง

![](/files/-LnILBKOVoV8DON-bnRg)

หรือถ้าจะเขียนเป็นภาพแบบเต็มๆที่ถูกต้องก็จะได้รูปแบบนี้

![](/files/-LoCcrr7seGdl-D2cdok)

เพียงเท่านี้เราก็สามารถแก้ปัญหาโจทย์นี้ได้เรียบร้อยแล้ว แถมเมื่อเราทำงานกับโรงงานพวกนี้ เรายังสามารถใช้ **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 %}

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

## 🤔 Abstract Factory คือไย ?

เจ้าตัวนี้มันจะคล้ายกับ [🏭 Factory Method Pattern](https://saladpuk.gitbook.io/learn/beginner-1/design-patterns/creational/factory-method-pattern#factory-method) มาก ดังนั้นผมจะเขียนจุดที่มันต่างกันของ Abstract Factory ไว้ใน Quote นะครับ

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

ในบางทีการสร้าง 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 ที่มันต้องไปด้วยกันเป็นเซตเราจะรู้ได้ยังไงว่าเราสร้างมันได้ถูก ไม่ได้เอาเซตอื่นๆมาปนกันมั่ว?

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

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

> **จุดที่เป็นเรื่องเฉพาะของเรื่องนี้**\
> object ที่เราสร้างมาจาก Factory เดียวกันมันจะเป็นของเซตเดียวกัน

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

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

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

![](/files/-LofNorUIgEqRR_YWKgn)

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

![](/files/-LofPlyIABcEQqJm0a_6)

และเวลาใช้งานจริงๆ **Product ประเภทเดียวกันก็อาจจะมีหลายแบบก็ได้** เช่น Slime ยังมี Drops กับ Poporing ไรงี้ ดังนั้นก็จะได้ภาพออกมาราวๆนี้

![](/files/-LngjMU9A9gshGTjvc10)

คราวนี้ถ้าเรา**มี Product หลายๆประเภท** และแต่ละประเภทก็มีหลายๆแบบด้วยนะ เราก็จะได้ภาพออกมาราวนี้ๆ

![](/files/-Lo3yCHL-KdDfpshKCn3)

และในบางที Product ที่อยู่ต่างประเภทกัน ก็อาจจะถูกจัดไว้ในเซตเดียวกันก็ได้ เช่น เซตธาตุไฟ เราก็จมี Slime ธาตุไฟ กับ หมาป่าธาตุไฟไรงี้

![ของต่างประเภทกัน ก็อาจมีความสัมพันธ์ที่จัดไว้ในเซตเดียวกันได้นะ](/files/-LsfRS7HdAkVgPS3YBgY)

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

![](/files/-LumiwTubkLtxeht-HVx)

แต่ตัว Factory เองนั้นมันไม่รู้หรอกว่ามันจะต้องสร้างอะไรออกมา ดังนั้นมันเลยต้องปล่อยให้เป็นหน้าที่ของคลาสระดับล่าง ที่รู้ว่ามันจะสร้าง product อะไรมาให้ ดังนั้นเลยทำให้เจ้า Factory ของเราคงอยู่แค่ในสภาพ **Interface** ก็เพียงพอแล้วนั่นเอง

> นี่แหละคือที่มาของคำว่า **Abstract Factory** เพราะตัวมันเองคือ **โครงร่างของโรงงานผลิต** เพียงเท่านั้น

![](/files/-LrXP1y-pmSsTz1inHXV)

ดังนั้นคลาสระดับล่างที่รู้ว่าจะต้องสร้างอะไร ก็รับผิดชอบ Implement เจ้า Abstract Factory นี้ไปซะ

![](/files/-LofFsz2B0rrpQd2GwuV)

และเมื่อมันรู้ว่าจะต้องสร้าง product อะไรออกมา มันจะต้อง **สร้าง product ที่อยู่ในเซตเดียวกัน** ออกมาด้วยนะ

![](/files/-M1lfss7z-6oIEjUoi44)

ซึ่งทั้งหมดที่ร่ายยาวมานั่นก็คือแนวทางในการออกแบบที่ชื่อว่า **Abstract Factory Pattern** นั่นเอง ซึ่งหน้าตาของมันก็จะประมาณรูปด้านบนนั่นแหละ แต่ขอเติมสัญลักษณ์ให้เข้าใจง่ายๆหน่อยนึงตามรูปด้านล่างละกันนะ

![](/files/-M1VE7Q9ULvsjCi0vF8b)

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

![](/files/-Lo3ySdXRqQr2NOzhZST)

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

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

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

{% hint style="warning" %}
ตัวอย่างพร้อมเหตุผลว่าทำไมถึงควรใช้ และมันมาแก้ปัญหาเรื่องอะไรบ้างของ Abstract Factory Pattern นั้นส่วนใหญ่มันจะเหมือนกับ Factory Method Pattern ซึ่งผมเขียนไว้ในบทความของ factory method แล้วยาวม๊วก เลยไม่อยากจะ copy มาใส่ตรงนี้ ดังนั้นรบกวนเพื่อนๆกดลิงค์ด้านล่างไปอ่านเอาละกันนะ แล้วจะเห็นภาพขึ้นเยอะเลยว่าทำไมถึงควรจะใช้มัน\
[**Factory Method Pattern 🤔 ทำไมต้องใช้ด้วย ?**](https://saladpuk.gitbook.io/learn/beginner-1/design-patterns/creational/factory-method-pattern#undefined-1)
{% endhint %}

### 😱 สร้าง object ผิดเซต

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

```csharp
var dog = new Dog();
var cat = new Cat();
var pig = new Pig();
var tiger = new Tiger();
```

ซึ่งจากโค้ดด้านบน มันมีอะไรบอกไหมว่า **หมา, แมว, หมู, เสือ** พวกนี้มันมาจากเซตเดียวกันทั้งหมด? ซึ่งกว่าจะรู้ก็อาจจะไปแสดงผลให้ลูกค้าเห็นประมาณนี้แล้วก็ได้

![รู้สึกว่า เสือ มันไม่เข้าพวกไหม ? ... เพราะมันมาจากคนละเซตยังไงล่ะ](/files/-LpkVH9QYSL3NTZEJWQW)

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

อย่างที่บอกไปว่าเจ้าตัวนี้มันคล้ายกับ Factory Method Pattern ดังนั้นส่วนใหญ่ไปดูได้จากตัวนั้นเลย ดังนั้นเราจะมาตอบข้อดีเฉพาะของ Abstract Factory กันดีกว่า

### 👨‍🔧 ~~สร้าง object ผิดเซต~~

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

```csharp
var dog = flatArtFactory.CreateADog();
var cat = flatArtFactory.CreateACat();
var pig = flatArtFactory.CreateAPig();
var tiger = flatArtFactory.CreateATiger();
```

ซึ่งถ้ามันยังสร้างผิดอีก นั่นแสดงว่าตัวที่ implement interface นั้นไปสร้าง object ผิดตัว ดังนั้นถ้าเราแก้มันเสร็จปุ๊ป ทุกจุดที่เรียกใช้มันก็จะไม่มี bug ตัวนี้อีกแล้วนั่นเอง (ดีกว่าไปไล่เช็คทุกจุดยังไงล่ะ)

![](/files/-LoARj6y4utNrtO8TgiO)

## ⚔️ Abstract Factory vs Factory Method

ผมเชื่อว่าถ้าใครได้ดู **Factory Method Pattern** กับ **Abstract Factory Pattern** แล้ว ก็อาจจะ งงๆ กันอยู่นะว่ามันเหมือนหรือต่างอะไรกันบ้าง ดังนั้นตรงนี้จะมาไขข้อข้องใจกันครัช

* วัตถุประสงค์ของทั้ง 2 ตัวนั้น เหมือนกันคือ **ช่วยสร้าง object** ที่จะเกิดขึ้นจากการใช้คำสั่ง `new` ตรงๆ
* **Factory Method Pattern** จะแก้ปัญหาผ่าน **Inheritance** โดยให้ Sub class เป็นคนจัดการ
* **Abstract Factory Pattern** จะแก้ปัญหาผ่าน **Composition** โดยใช้คลาสนั้นๆไป implement เอาเอง
* **Factory Method Pattern** จะสร้าง object โดยไม่ได้มีเรื่องเซตมาเกี่ยวข้อง
* **Abstract Factory Pattern** จะสร้าง object โดยคำนึงถึงเรื่องเซตด้วยเสมอ และ มันจะต้องสามารถสร้าง product อื่นๆที่อยู่ภายในเซตเหล่านั้นได้ด้วย

{% hint style="info" %}
**เกร็ดความรู้**\
Abstract Factory Pattern นั้นภายในการทำงานจริงๆ ส่วนใหญ่ก็จะไปเรียกใช้งาน Factory Method Pattern มาทำงานต่ออีกทีนึงเช่นกัน เพราะมันก็จะช่วยลด Coupling ลง และยังช่วยทำให้โปรเจคของเราเปิดรับของต่างๆมากยิ่งขึ้นนั่นเอง
{% endhint %}

## 🎯 บทสรุป

### 👍 ข้อดี

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

### 👎 ข้อเสีย

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

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

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

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

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