/ OOD

[365 วันแห่งโปรแกรม #day50] Simple Factory, Factory Method, Abstract Factory

วันที่ห้าสิบของ ‪#‎365วันแห่งโปรแกรม ห่างหายกันไปหลายวันครับหลังจากที่เราคุยเรื่อง Design Pattern กัน ที่หายไปก็ไม่ใช่เพราะอะไรนอกจากว่าเรื่องที่จะพูดในวันนี้มันทำให้สับสบมาก จนเริ่มไม่ถูก และเรื่องนั้นก็คือ Simple Factory, Factory Method, Abstract Factory pattern


วันนี้ขอเสนอรวดเดียว 3 pattern เลยครับ ก็คือ Simple Factory, Factory Method, และ Abstract Factory ซึ่งเป็นแพทเทอร์นในกลุ่มของ Creational patterns ที่แก้ปัญหาเกี่ยวกับความซับซ้อนของการสร้าง object นั่นเอง

Factory

ในเชิงโปรแกรมมิ่งคำว่า Factory จะมีความหมายว่า

a factory is an object for creating other objects

แปลเป็นไทยก็ตรงๆ คือ object ที่ใช้สร้าง object นั่นเอง

แล้วทำไมต้องใช้ Factory ล่ะ?

บ่อยครั้งการส้ราง object ขึ้นมานั้นเป็นเรื่องที่ซับซ้อน เช่น มีเรื่องที่ต้องตัดสินใจจำนวนมาก มีพารามิเตอร์ที่ต้องใส่จำนวนมาก หรือมีการสร้าง object แบบเดียวกันหลายแห่ง หรืออื่นๆ ดังนั้นการรวมส่วนสร้าง object ที่ซับซ้อนเหล่านี้ไว้ใน composing object (คลาสที่เอา object ไปใช้) นั้นดูไม่ค่อยเหมาะสมเท่าไหร่ factory เลยบอกว่า งั้นเราก็สร้าง method ขึ้นมาครอบส่วนสร้าง object ไว้เลยสิ

Simple Factory

เป็น factory แบบที่ง่ายที่สุดใน 3 ตัว ก็คือแค่สร้าง method สำหรับใช้สร้าง object ไว้ แค่นั้นเลย โดย factory นี้อาจจะคืน object ได้หลากหลายชนิด (ที่ derived มาจาก parent เดียวกัน)

โดยทั่วไปแล้วเรามักจำสร้างเป็น static method เพื่อความสะดวกในการใช้งาน และใช้พารามิเตอร์สำหรับระบุว่าจะสร้าง object ของ sub-type ไหน

Simple Factory

ขั้นตอนการทำงาน

  1. client เรียกใช้งาน method ของ Simple Factory

  2. Simple Factory ตัดสินใจจากพารามิเตอร์ที่ได้รับ แล้วสร้าง object ขึ้น

  3. Simple Factory ส่งคืน object ที่สร้างขึ้นให้ client

โดยทั่วไป client จะไม่ทำการรับค่าเป็น concrete-type แต่จะใช้ abstraction ในการรับแทน เนื่องจาก client จะไม่รู้เลยว่า จะได้ type ไหนคืนจาก Factory

class FruitFactory {
    public static Fruit MakeFruit(string name) {
        switch(name.ToLower()){
            case "apple" : return new Apple();
            case "orange" : return new Orange();
            default : return new UnknownFruit();
        }
    }
}

Fruit fruit = FruitFactory.MakeFruit("apple");

*Simple Factory ไม่ถือว่าเป็น "official" design pattern

Factory method pattern

Factory method pattern ก็คล้ายๆ กับ Simple Factory แต่จะกำหนดให้ Factory เป็น abstraction แล้วให้เราทำการ derived Factory นั้นไป implement method สำหรับสร้าง object แต่ละแบบ

หัวใจหลักของวิธีนี้คือลดการผูกมัดระหว่างคลาสลงไปอีก โดย client จะไม่จำเป็นต้องรู้ว่ากำลังใช้งานจาก Factory ไหน เพื่อสร้างอะไร แต่จะรู้ว่าจะสร้าง object ด้วย Factory นั้นได้อย่างไร (depends on abstraction และ inject factory เข้ามา)

Factory method pattern

จากภาพจะเห็นว่า Client จะ depends on Creator และ Interface ของ object ที่ต้องการ ซึ่งก็เป็น abstraction ทั้งคู่ แล้วเราก็จะมีการสร้างคลาส ที่ derived จาก Creator เพื่อทำหน้าที่สร้าง object ที่ derived มาจาก type ที่ Client ต้องการ

ขั้นตอนการทำงานก็จะเป็น

  1. Client เก็บ reference ของ Creator ที่ชี้ไปยัง implementation ของ Creator ที่ได้รับมาจากการ inject หรือไม่ก็สร้างขึ้นเอง(ไม่แนะนำ)

  2. เรียกใช้ method ของ Creator สำหรับสร้าง object

  3. object ของ Creator ทำการส้ราง object ที่ Client ต้องการ

  4. Creator ส่งคืน object ที่สร้างไปยัง Client

ด้วยวิธีนี้แล้วจะทำให้เราลดการ couple กับ implementation ไปได้ ดังนั้นถ้าเราแก้ส่วน implementation ของ Creator ก็ไม่จำเป็นต้องมาแก้ที่ Client อีก ต่างจาก Simple Factory ที่อ้างถึงตัว Factory ตรงๆ

interface FruitFactory{
    public Fruit MakeFruit();
}

class AppleFactory : FruitFactory{
    public override Fruit MakeFruit(){
        return new Apple();
    }
}

class OrangeFactory : FruitFactory{
    public override Fruit MakeFruit(){
        return new Orange();
    }
}

class Client{
    private FruitFactory fruitFactory;

    public Client(FruitFactory fruitFactory){
        this.fruitFactory = fruitFactory;
    }

    public void DoSomething(){
        Fruit fruit = fruitFactory.MakeFruit();
    }
}

สรุปคือวิธีนี้จะไม่ใช้ parameter ในการระบุ type ที่ต้องการแล้ว (จริงๆ ทำได้ แต่ไม่นิยม) และทุก object ที่สร้างมาจาก Implementation ของ Factory เดียวกันจะมี type เดียวกัน

Abstract factory pattern

Abstract factory pattern เป็นแพทเทิร์นที่ใหญ่ที่สุดในสามตัวนี้ แต่มันไม่ได้ยากอย่างที่คิด เพราะจริงๆ แล้วเจ้าตัวนี้ก็เหมือนกับ Factory method pattern เลย ความแตกต่างอยู่ที่ปกติแล้ว Factory method pattern จะมี method สำหรับใช้สร้าง object เพียงชนิดเดียว แต่ Abstract factory pattern ยอมให้มีหลาย method สำหรับสร้าง object หลายชนิดได้ แต่มีข้อแม้ว่าจะต้องเป็น object ในกลุ่มเดียวกัน (ไม่ต้องมี parent ร่วมกัน แค่มีความสัมพันธ์กันก็พอ) เช่น โรงงานรถยนต์ยี่ห้อหนึ่ง จะผลิตรถยนต์กี่ประเภทก็ได้ ไม่ต้องสร้างโรงงานใหม่

Abstract factory pattern

จากภาพจะเห็นว่าเหมือน Factory method pattern เลย ต่างตรงที่ใน Factory มี method สำหรับสร้าง object อยู่ 2 ชนิด ดังนั้นขั้นตอนการทำงานก็จะเหมือนๆ เดิม

  1. Client เก็บ reference ของ Factory ที่ชี้ไปยัง implementation ของ Factory ที่ได้รับมาจากการ inject หรือไม่ก็สร้างขึ้นเอง(ไม่แนะนำ)

  2. เรียกใช้ method ของ Factory สำหรับสร้าง object ในชนิดที่ต้องการ

  3. object ของ Factory ทำการส้ราง object ที่ Client ร้องขอ

  4. object ของ Factory ทำการส่งคืน object ที่สร้างกลับไป

สรุปคือเหมือนกันเด๊ะ เว้นแต่ว่าสร้างได้หลายอย่างใน Factory เดียว

public abstract class CarFactory
{
    public abstract SportsCar CreateSportsCar();
    public abstract FamilyCar CreateFamilyCar();
}

public class MercedesFactory : CarFactory
{
    public override SportsCar CreateSportsCar()
    {
        return new MercedesSportsCar();
    }

    public override FamilyCar CreateFamilyCar()
    {
        return new MercedesFamilyCar();
    }
}

public class Driver
{
    private CarFactory _carFactory;
    private SportsCar _sportsCar;
    private FamilyCar _familyCar;

    public Driver(CarFactory carFactory)
    {
        _carFactory = carFactory;
        _sportsCar = _carFactory.CreateSportsCar();
        _familyCar = _carFactory.CreateFamilyCar();
    }
}

References

Factory (object-oriented programming)

Factory method pattern

Abstract factory pattern

Motivation for Simple Factory and Factory Method Pattern

Simple Factory vs. Factory Method vs. Abstract Factory

Design Patterns: Factory vs Factory method vs Abstract Factory

#‎day50 #365วันแห่งโปรแกรม ‪#‎โครงการ365วันแห่ง‬...