#Facade Pattern คืออะไร

Facade Pattern เป็น Structural Design Pattern ที่จัดเตรียมอินเทอร์เฟซระดับสูงเพียงจุดเดียวเพื่อเรียกใช้งานระบบย่อย (subsystems) ที่ซับซ้อน ช่วยให้โค้ดฝั่งผู้ใช้ (client) ไม่จำเป็นต้องทราบลำดับการทำงานภายในทั้งหมด

ตัวอย่างเช่น ระบบสั่งซื้อสินค้าอาจต้องตรวจสอบสต็อก ชำระเงิน และจัดส่งสินค้า Facade จะประสานงานขั้นตอนเหล่านี้ผ่านเมธอดเดียว เช่น placeOrder()

#โครงสร้าง

classDiagram
    class Client
    class OrderFacade {
      +placeOrder(productId, amount) boolean
    }
    class InventoryService {
      +isAvailable(productId) boolean
    }
    class PaymentService {
      +charge(amount) boolean
    }
    class ShippingService {
      +schedule(productId) void
    }
    Client --> OrderFacade
    OrderFacade --> InventoryService
    OrderFacade --> PaymentService
    OrderFacade --> ShippingService

#ตัวอย่าง Workshop: ระบบสั่งซื้อสินค้า

ตัวอย่างต่อไปนี้ใช้ Java 17 ขึ้นไป โดยแยกคลาสเป็นไฟล์เดียวเพื่อทดลองได้ทันที

สร้างไฟล์ Main.java:

class InventoryService {
    public boolean isAvailable(String productId) {
        System.out.println("Checking inventory: " + productId);
        return true;
    }
}

class PaymentService {
    public boolean charge(double amount) {
        System.out.println("Charging payment: " + amount);
        return true;
    }
}

class ShippingService {
    public void schedule(String productId) {
        System.out.println("Scheduling shipping: " + productId);
    }
}

class OrderFacade {
    private final InventoryService inventory;
    private final PaymentService payment;
    private final ShippingService shipping;

    public OrderFacade(InventoryService inventory,
                       PaymentService payment,
                       ShippingService shipping) {
        this.inventory = inventory;
        this.payment = payment;
        this.shipping = shipping;
    }

    public boolean placeOrder(String productId, double amount) {
        if (amount <= 0 || !inventory.isAvailable(productId)) {
            return false;
        }
        if (!payment.charge(amount)) {
            return false;
        }
        shipping.schedule(productId);
        return true;
    }
}

public class Main {
    public static void main(String[] args) {
        OrderFacade facade = new OrderFacade(
            new InventoryService(),
            new PaymentService(),
            new ShippingService()
        );
        boolean success = facade.placeOrder("BOOK-001", 499.0);
        System.out.println("Order success: " + success);
    }
}

รันโปรแกรม:

javac Main.java
java Main

ผลลัพธ์:

Checking inventory: BOOK-001
Charging payment: 499.0
Scheduling shipping: BOOK-001
Order success: true

#ก่อนและหลังใช้ Facade

ก่อนใช้ Facade: Client เรียก InventoryService, PaymentService และ ShippingService เอง และต้องจัดการลำดับขั้นตอนทั้งหมด

หลังใช้ Facade: Client เรียก facade.placeOrder(...) เพียงจุดเดียว ทำให้โค้ดอ่านง่ายและลดการผูกติดกับระบบย่อย

#ข้อดี

  • ลดความซับซ้อนของการเรียกใช้ระบบย่อย
  • ลด coupling ระหว่าง client กับ subsystem
  • รวม workflow ที่ใช้งานบ่อยไว้ในจุดเดียว
  • ทดสอบการประสานงานได้ง่ายขึ้นเมื่อฉีด dependencies ผ่าน constructor

#ข้อควรระวัง

  • Facade อาจขยายเป็น God Object หากรวมความรับผิดชอบมากเกินไป
  • Facade ไม่ได้แทนที่ API ของระบบย่อยทั้งหมด Client ยังเรียก subsystem โดยตรงได้เมื่อจำเป็น
  • ตัวอย่างนี้เป็นการจำลองเพื่อเรียนรู้: ระบบจริงควรมีการจัดการ transaction, การชำระเงินซ้ำ, ความล้มเหลวของการจัดส่ง และการชดเชยธุรกรรมอย่างเหมาะสม

#Facade ต่างจาก Adapter อย่างไร

Pattern วัตถุประสงค์
Facade ทำให้การใช้งานหลาย subsystem ง่ายขึ้นผ่านอินเทอร์เฟซระดับสูง
Adapter แปลงอินเทอร์เฟซที่เข้ากันไม่ได้ให้ใช้งานร่วมกันได้
Mediator จัดระเบียบการสื่อสารระหว่างออบเจ็กต์หลายตัว

#เมื่อใดควรใช้

ใช้ Facade เมื่อระบบมีขั้นตอนย่อยหลายส่วนและต้องการให้ client ใช้งานผ่าน API ที่กระชับ เช่น ระบบสั่งซื้อสินค้า ระบบส่งอีเมลพร้อมแนบไฟล์ ระบบประมวลผลวิดีโอ หรือการเรียกบริการหลายตัวใน backend

#สรุป

Facade Pattern ช่วยซ่อนความยุ่งยากของการประสานงาน subsystem และสร้างจุดเข้าถึงที่ใช้งานง่าย เหมาะกับ Java Application ที่ต้องการแยกส่วน client ออกจากรายละเอียดการทำงานภายใน โดยยังคงเปิดทางให้ใช้ subsystem โดยตรงได้เมื่อจำเป็น