ข้ามไปยังเนื้อหา

Abstract Factory

Abstract Factory ให้ interface เดียวสำหรับสร้างตระกูลของ product ที่เกี่ยวข้องกันโดยไม่ต้องเอ่ยชื่อ concrete class ของ product รับประกันว่า product ที่คุณได้มาจะอยู่ในตระกูลเดียวกันเสมอ

ลองพิจารณา UI kit ข้ามแพลตฟอร์ม หน้าต่างหนึ่งต้องการปุ่มและ checkbox และในทุกแพลตฟอร์ม widget เหล่านั้นต้องมีหน้าตาและพฤติกรรมที่สอดคล้องกัน ปุ่มแบบ macOS ที่จับคู่กับ checkbox แบบ Windows ย่อมผิดเพี้ยน อันตรายคือการปนตระกูลกัน code ที่สร้างแต่ละ widget แยกกันอาจประกอบชุดที่ไม่สอดคล้องกันได้ง่าย ๆ

Abstract Factory แก้ปัญหานี้ด้วยการยกทั้งตระกูลขึ้นมาเป็น interface เดียว GuiFactory ประกาศ createButton และ createCheckbox แต่ละ concrete factory (MacFactory, WindowsFactory) คืน widget จากแพลตฟอร์มเดียวเท่านั้น แอปพลิเคชันรับ factory มาครั้งเดียว แล้วจึงขอชิ้นส่วนต่าง ๆ จาก factory ตัวนั้น เพราะทุกชิ้นมาจาก factory เดียวกัน จึงรับประกันได้ว่าทุกชิ้นเข้ากันได้ และการสลับแพลตฟอร์มก็หมายถึงการสลับ object เพียงตัวเดียว

เปรียบเทียบกับ Factory Method Factory Method ว่าด้วย product หนึ่ง ตัวที่เลือกผ่านการ subclass และ override method เดียว ความแปรผันอาศัยการสืบทอด ส่วน Abstract Factory ว่าด้วย ตระกูลของ product ที่เลือกผ่านการประกอบเข้าใน factory object โดยทั่วไปคุณถือ factory instance และเรียก creation method หลายตัวบน instance นั้น พูดอีกอย่างคือ Factory Method ตอบคำถาม “ตัวไหน?” ผ่าน subclass ส่วน Abstract Factory ตอบคำถาม “ชุดไหน?” ผ่าน object ที่สลับได้ และ method ต่าง ๆ ของ Abstract Factory ก็มักเป็น factory method เสียเอง

classDiagram
  class GuiFactory {
    <<interface>>
    +createButton() Button
    +createCheckbox() Checkbox
  }
  class MacFactory {
    +createButton() Button
    +createCheckbox() Checkbox
  }
  class WindowsFactory {
    +createButton() Button
    +createCheckbox() Checkbox
  }
  class Button {
    <<interface>>
    +render() string
  }
  class Checkbox {
    <<interface>>
    +render() string
  }
  class MacButton
  class WindowsButton
  class MacCheckbox
  class WindowsCheckbox
  GuiFactory <|.. MacFactory
  GuiFactory <|.. WindowsFactory
  Button <|.. MacButton
  Button <|.. WindowsButton
  Checkbox <|.. MacCheckbox
  Checkbox <|.. WindowsCheckbox
  MacFactory ..> MacButton
  MacFactory ..> MacCheckbox
  WindowsFactory ..> WindowsButton
  WindowsFactory ..> WindowsCheckbox
แต่ละ concrete factory สร้างหนึ่งตระกูลของ widget ที่สอดคล้องกัน
  • Abstract Factory (GuiFactory) — ประกาศ creation method หนึ่งตัวต่อ product หนึ่งตัวในตระกูล
  • Concrete Factories (MacFactory, WindowsFactory) — แต่ละตัวผลิตตระกูลที่ครบถ้วนและสอดคล้องกันภายใน
  • Abstract Products (Button, Checkbox) — interface ที่ client พึ่งพา
  • Concrete Products (MacButton, WindowsCheckbox, …) — implementation เฉพาะแพลตฟอร์ม
  • Client — รับ factory มาและสร้าง UI จากชิ้นส่วนที่คืนกลับมา โดยไม่เคยเอ่ยชื่อ concrete class

GuiFactory ที่ให้ปุ่มและ checkbox ที่เข้าคู่กันต่อหนึ่งแพลตฟอร์ม

interface Button {
render(): string;
}
interface Checkbox {
render(): string;
}
class MacButton implements Button {
render() {
return '[ macOS button ]';
}
}
class MacCheckbox implements Checkbox {
render() {
return '[ macOS checkbox ]';
}
}
class WindowsButton implements Button {
render() {
return '[ Windows button ]';
}
}
class WindowsCheckbox implements Checkbox {
render() {
return '[ Windows checkbox ]';
}
}
interface GuiFactory {
createButton(): Button;
createCheckbox(): Checkbox;
}
class MacFactory implements GuiFactory {
createButton(): Button {
return new MacButton();
}
createCheckbox(): Checkbox {
return new MacCheckbox();
}
}
class WindowsFactory implements GuiFactory {
createButton(): Button {
return new WindowsButton();
}
createCheckbox(): Checkbox {
return new WindowsCheckbox();
}
}
function buildForm(factory: GuiFactory): string {
return `${factory.createButton().render()} ${factory.createCheckbox().render()}`;
}
console.log(buildForm(new MacFactory()));
console.log(buildForm(new WindowsFactory()));
  • ข้อดี: product จาก factory เดียวรับประกันว่าอยู่ในตระกูลเดียวกัน ไม่มีการจับคู่ที่ผิดเพี้ยน
  • ข้อดี: การสลับทั้งตระกูลเป็นการเปลี่ยนแปลงเพียงบรรทัดเดียว: ส่ง factory ตัวอื่นเข้าไป
  • ข้อดี: client พึ่งพาเฉพาะ abstract product interface เท่านั้น ไม่เคยพึ่งพา concrete class
  • ข้อเสีย: การเพิ่ม product ใหม่ เข้าตระกูลหมายถึงการต้องเปลี่ยน factory interface และทุก concrete factory
  • ข้อเสีย: สำหรับ product เดียวหรือตระกูลเดียว pattern นี้หนักกว่า Factory Method หรือ constructor ธรรมดา
  • Factory Method เป็นญาติที่เรียบง่ายกว่าซึ่งจัดการ product เดียว และมักถูกใช้ implement แต่ละ method ของ Abstract Factory
  • Singleton มักถือ factory ที่เลือกไว้เพื่อให้ instance เดียวถูกแชร์ทั่วทั้งแอป
Abstract FactoryFactory MethodBuilder
จุดประสงค์สร้าง product เป็นชุดสร้าง product เดี่ยวสร้าง object ซับซ้อนทีละขั้น
แปรผันด้วยobject (factory)subclassdirector + builder
client รู้จักinterface ของ factoryinterface ของ creatorinterface ของ builder
เมื่อใช้ต้องการ product ที่เข้าชุดแค่ต้องการ defer การสร้างต้องการ config ที่ซับซ้อน
Abstract Factory รับประกันอะไรเป็นหลัก?
Abstract Factory ต่างจาก Factory Method อย่างไร?
ต้นทุนหลักของการเพิ่ม product ชนิดใหม่เอี่ยมเข้าตระกูลคืออะไร?