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

Factory Method

Factory Method นิยาม interface สำหรับสร้าง object แต่ปล่อยให้แต่ละ subclass ตัดสินใจว่าจะสร้าง instance ของ concrete class ตัวไหน เพื่อให้ workflow โดยรอบยังคงเดิม ส่วน product เปลี่ยนแปลงไป

คุณมีกระบวนการที่เหมือนกันทุกประการไม่ว่าจะทำงานกับอะไร ยกเว้นรายละเอียดเดียว: ชนิดของ object ที่ทำงานด้วย นักวางแผนโลจิสติกส์จัดตารางการจัดส่งด้วยวิธีเดียวกันไม่ว่าสินค้าจะเดินทางโดยรถบรรทุกหรือเรือ แต่ต้องผลิตยานพาหนะชนิดที่ถูกต้อง ถ้าคุณ hard-code new Truck() เข้าไปในตัววางแผน การรองรับเรือก็หมายถึงการต้องไปแก้ตัววางแผนและเสี่ยงต่อตรรกะที่แชร์กัน

Factory Method ดึงขั้นตอนเดียวที่แปรผันนั้นออกมาเป็น method ที่ override ได้ class หลักเขียน workflow ในรูปของ abstract product และเรียก factory method ของตัวเองเพื่อให้ได้ concrete object มา subclass override เฉพาะ factory method เท่านั้น workflow ถูกสืบทอดมาโดยไม่เปลี่ยนแปลง ส่วน product ถูกเลือกโดย subclass สิ่งนี้รักษาหลัก open/closed เอาไว้ครบถ้วน คุณเพิ่ม product ใหม่ด้วยการเพิ่ม subclass ไม่ใช่ด้วยการแก้ code ที่มีอยู่

classDiagram
  class Product {
    <<interface>>
    +deliver() string
  }
  class Truck {
    +deliver() string
  }
  class Ship {
    +deliver() string
  }
  class Logistics {
    +planDelivery() string
    +createTransport()* Product
  }
  class RoadLogistics {
    +createTransport() Product
  }
  class SeaLogistics {
    +createTransport() Product
  }
  Product <|.. Truck
  Product <|.. Ship
  Logistics <|-- RoadLogistics
  Logistics <|-- SeaLogistics
  Logistics ..> Product : creates
  RoadLogistics ..> Truck
  SeaLogistics ..> Ship
Creator เรียก factory method ของตัวเอง ส่วน subclass เป็นผู้เลือก Product
  • Product — interface ที่ factory method คืนกลับมา workflow พึ่งพาเฉพาะ type นี้เท่านั้น
  • Concrete Products (Truck, Ship) — implementation เฉพาะที่ workflow ไม่รับรู้
  • Creator (Logistics) — ถือ workflow ที่แชร์กัน (planDelivery) และประกาศ abstract factory method (createTransport)
  • Concrete Creators (RoadLogistics, SeaLogistics) — override factory method เพื่อคืน product ตัวใดตัวหนึ่งโดยเฉพาะ

นักวางแผน Logistics ที่ขั้นตอน planDelivery ถูกแชร์ ส่วน subclass เป็นผู้เลือกยานพาหนะ

interface Transport {
deliver(): string;
}
class Truck implements Transport {
deliver(): string {
return 'Delivering by road in a truck.';
}
}
class Ship implements Transport {
deliver(): string {
return 'Delivering by sea in a ship.';
}
}
abstract class Logistics {
// Shared workflow, written against the Transport interface.
planDelivery(): string {
const transport = this.createTransport();
return `Planned. ${transport.deliver()}`;
}
// The factory method subclasses override.
protected abstract createTransport(): Transport;
}
class RoadLogistics extends Logistics {
protected createTransport(): Transport {
return new Truck();
}
}
class SeaLogistics extends Logistics {
protected createTransport(): Transport {
return new Ship();
}
}
console.log(new RoadLogistics().planDelivery());
console.log(new SeaLogistics().planDelivery());
  • ข้อดี: workflow ที่แชร์กันไม่เคยเอ่ยชื่อ concrete product การเพิ่ม product ใหม่จึงหมายถึงการเพิ่ม subclass ไม่ใช่การแก้ผู้เรียก
  • ข้อดี: การสร้าง product อยู่ใน method เดียวที่ override ได้ ค้นหาและแทนที่ได้ง่าย
  • ข้อเสีย: product variant ใหม่แต่ละตัวมักต้องการ creator subclass ใหม่ ซึ่งอาจทำให้ class เล็ก ๆ เพิ่มจำนวนขึ้น
  • ข้อเสีย: สำหรับ product ชนิดเดียว pattern นี้เกินความจำเป็น constructor หรือฟังก์ชันธรรมดาชัดเจนกว่า
  • Abstract Factory มักใช้ factory method หลายตัวร่วมกันเพื่อสร้างตระกูล product
  • Prototype เป็นทางเลือกเมื่อ object ใหม่ควรคัดลอกจากของที่มีอยู่แทนที่จะสร้างใหม่หมด
Factory MethodAbstract Factory
สร้างอะไรproduct เดี่ยวหลาย product ที่เข้าชุดกัน
แปรผันด้วยsubclassobject
client รู้จักcreatorfactory
เพิ่ม product ใหม่เพิ่ม subclassเพิ่ม factory ใหม่ทั้งชุด
ใน Factory Method ใครเป็นผู้ตัดสินใจว่าจะสร้าง instance ของ concrete class ตัวไหน?
อะไรที่คงเดิมในทุก concrete creator?
ทำไม Factory Method จึงสนับสนุนหลัก open/closed?