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

Template Method

Template Method นิยามโครงสร้างโดยรวมของอัลกอริทึมไว้ที่เดียว คือ template method แล้วเลื่อนขั้นตอนที่เลือกไว้ให้ subclass โครงร่าง รวมถึงลำดับขั้นตอนที่ตรึงไว้ อยู่ใน class หลัก ส่วนที่ผันแปรคือ hook ที่ override ได้ subclass เปลี่ยน อะไร ที่เกิดขึ้นในแต่ละขั้นโดยไม่เปลี่ยน ลำดับ

งานหลายอย่างมีรูปร่างเดียวกันแต่ต่างกันที่รายละเอียด pipeline ประมวลผลข้อมูลมักจะอ่าน input แจงข้อมูล แปลงเรคคอร์ด แล้วเขียนผลลัพธ์เสมอ แต่แหล่งข้อมูล CSV กับแหล่ง JSON แจงต่างกัน และรายงานหนึ่งอาจกรองแถวที่อีกรายงานเก็บไว้ การคัดลอก pipeline ทั้งชุดสำหรับแต่ละตัวแปรทำให้โครงนั่งร้านของการอ่าน-เขียนซ้ำซ้อน และชวนให้สำเนาเหล่านั้นแยกออกจากกันเมื่อเวลาผ่านไป

ส่วนที่ตรึงไว้ในที่นี้คือ ลำดับของขั้นตอน ส่วนที่ผันแปรคือ เนื้อหา ของบางขั้น Template Method จับลำดับไว้ครั้งเดียวใน method ฐานที่เรียกขั้นตอนแต่ละขั้นตามลำดับ subclass override เพียงขั้นตอนที่ต่างกัน การควบคุมการไหลเป็นของ class หลัก ดังนั้นทุกตัวแปรจึงรันขั้นตอนในลำดับที่พิสูจน์แล้วเหมือนกัน เป็นตัวอย่างของการกลับด้านการควบคุม (inversion of control) แบบ “อย่าโทรหาเรา เราจะโทรหาคุณเอง”

classDiagram
  class Pipeline {
    <<abstract>>
    +run(raw) string
    #parse(raw)* 
    #transform(rows)
    #format(rows)* 
  }
  class CsvPipeline {
    #parse(raw)
    #format(rows)
  }
  class JsonPipeline {
    #parse(raw)
    #format(rows)
  }
  Pipeline <|-- CsvPipeline
  Pipeline <|-- JsonPipeline
Pipeline ฐานตรึง run() ไว้ subclass override ขั้นตอน parse และ format
  • Abstract class — นิยาม template method ที่เรียกขั้นตอนตามลำดับตายตัว บางขั้นตอนเป็น abstract (ต้อง override) บางขั้นตอนเป็นค่าดีฟอลต์ที่เป็นรูปธรรม (อาจ override ได้) และตัว template method เองมักไม่ถูก override
  • Concrete class — override ขั้นตอนที่ผันแปรได้เพื่อเจาะจงอัลกอริทึม
  • Client — เรียก template method แล้วได้อัลกอริทึมทั้งชุด ที่ถูกปรับแต่งโดย subclass ที่ใช้

pipeline ประมวลผลข้อมูลที่ method run ตรึงลำดับ read-parse-transform-format ไว้ ส่วน subclass จัดหาขั้นตอน parse และ format ภาษาที่ไม่มีการสืบทอดแบบคลาสสิกแสดงแนวคิดเดียวกันด้วยการประกอบ (composition) หรือฟิลด์ฟังก์ชัน

abstract class Pipeline {
// The template method: a fixed sequence of steps.
run(raw: string): string {
const rows = this.parse(raw);
const kept = this.transform(rows);
return this.format(kept);
}
protected abstract parse(raw: string): string[];
// A default step subclasses may override.
protected transform(rows: string[]): string[] {
return rows.filter((r) => r.length > 0);
}
protected abstract format(rows: string[]): string;
}
class CsvPipeline extends Pipeline {
protected parse(raw: string): string[] {
return raw.split('\n');
}
protected format(rows: string[]): string {
return rows.join(',');
}
}
const csv = new CsvPipeline();
console.log(csv.run('a\nb\n\nc')); // "a,b,c"
  • ข้อดี: ขจัดความซ้ำซ้อนโดยเก็บโครงร่างร่วมไว้ที่เดียว
  • ข้อดี: class หลักบังคับลำดับขั้นตอนที่ถูกต้อง subclass จึงไม่สามารถจัดลำดับใหม่โดยบังเอิญได้
  • ข้อดี: ตัวแปรใหม่ต้อง override เพียงขั้นตอนที่ต่างกันเท่านั้น
  • ข้อเสีย: pattern นี้พึ่งพาการสืบทอดซึ่งแข็งทื่อ subclass ถูกล็อกไว้กับอัลกอริทึมฐานตัวเดียว
  • ข้อเสีย: hook มากเกินไปทำให้ method ฐานติดตามได้ยาก และการ override ขั้นตอนผิดอาจทำให้สัญญาเสียหายอย่างแนบเนียน
  • Strategy บรรลุความผันแปรผ่านการประกอบ (composition) แทนการสืบทอด จึงสลับอัลกอริทึมทั้งชุดได้ในขณะรัน ขณะที่ Template Method ทำให้ขั้นตอนที่ตรึงไว้ผันแปร ณ เวลาสร้าง class ลูก
  • Factory Method มักเป็นขั้นตอน ภายใน template method โดยอัลกอริทึมฐานเรียกขั้นตอนการสร้างที่ override ได้
Template MethodStrategyFactory Method
mechanisminheritancecompositioninheritance
เปลี่ยนส่วนไหนstep ย่อยใน algorithmalgorithm ทั้งชุดขั้นตอนการสร้าง object
เวลาที่เลือกcompile time (subclass)runtime (inject)compile time (subclass)
ตัวอย่างtest setUp/tearDown, build pipelinepricing, sort, compressionLoggerFactory, RouterFactory

💡 หมายเหตุสำหรับ developer

Pattern นี้พบได้บ่อยใน:

  • JUnit / Jest lifecycle hooks (setUp/beforeEach, tearDown/afterEach) — framework กำหนดโครงของ test แต่ปล่อยให้ subclass เติมรายละเอียด
  • Django class-based views — base view กำหนดขั้นตอนของ request handling ทั่วไป ให้ subclass override เฉพาะส่วนที่ต่าง
  • Spring JdbcTemplate — จัดการ connection/transaction ให้อัตโนมัติ เหลือแค่ query logic ให้ผู้ใช้เติม
ตัว template method เองนิยามสิ่งใด?
subclass เปลี่ยนส่วนใด?
Template Method แสดงการกลับด้านการควบคุมแบบใด?
อะไรคือข้อจำกัดสำคัญเมื่อเทียบกับ Strategy?