รู้จักกับ Composite Design Pattern จัดการโครงสร้างข้อมูลซับซ้อนให้เป็นเรื่องง่าย

7 นาที 12 views บันทึกเป็น PDF
รู้จักกับ Composite Design Pattern จัดการโครงสร้างข้อมูลซับซ้อนให้เป็นเรื่องง่าย

เบื่อไหมที่ต้องเขียนโค้ดเช็คประเภทข้อมูลซ้ำๆ? มาเรียนรู้ Composite Design Pattern ที่ช่วยให้คุณจัดการวัตถุเดี่ยวและกลุ่มก้อนได้ด้วยคำสั่งเดียว โค้ดสะอาดขึ้นเยอะ!

ทำความรู้จักกับ Composite Design Pattern

เวลาเราเขียนโปรแกรม เรามักจะเจอปัญหาเรื่องการจัดการกลุ่มข้อมูลที่มีความซับซ้อน เช่น การทำระบบตะกร้าสินค้าที่มีทั้งสินค้าชิ้นเดียว และสินค้าที่เป็นชุดเซต ถ้าเราเขียนโค้ดแยกกันตลอดเวลา เราจะเหนื่อยมากเมื่อต้องเพิ่มฟีเจอร์ใหม่ๆ เข้าไป

Composite Design Pattern (รูปแบบการออกแบบเชิงโครงสร้างที่ช่วยจัดการกลุ่มวัตถุ) คือทางออกของปัญหานี้ มันช่วยให้เรามอง "ของชิ้นเดียว" กับ "กลุ่มของ" เป็นสิ่งเดียวกันได้ผ่านหน้าตาที่เหมือนกัน หรือที่เรียกว่า Interface (ข้อตกลงที่กำหนดว่าคลาสต้องมีฟังก์ชันอะไรบ้าง)

ลองจินตนาการถึงโครงสร้างต้นไม้ดูครับ กิ่งไม้หนึ่งกิ่งอาจจะมีใบไม้ติดอยู่ หรืออาจจะมีกิ่งย่อยแยกออกไปอีกก็ได้ แต่ไม่ว่าจะเป็นใบหรือกิ่ง ทั้งหมดต่างก็เป็นส่วนหนึ่งของต้นไม้เหมือนกัน การใช้แพทเทิร์นนี้จะช่วยให้โค้ดของคุณดูสะอาดและขยายต่อได้ง่ายขึ้นมากครับ

เมื่อไหร่ที่ควรใช้ Composite Pattern

เราควรใช้แพทเทิร์นนี้เมื่อโปรเจกต์ของคุณมีโครงสร้างแบบ Hierarchy (ลำดับชั้นเหมือนต้นไม้) เช่น ระบบไฟล์ในคอมพิวเตอร์ที่มีโฟลเดอร์ซ้อนโฟลเดอร์ หรือระบบองค์กรที่มีพนักงานและแผนกย่อย

ถ้าคุณพบว่าตัวเองต้องเขียนคำสั่งตรวจสอบประเภทข้อมูลบ่อยๆ เช่น "ถ้าเป็นสินค้าเดี่ยวให้ทำแบบนี้ แต่ถ้าเป็นชุดเซตให้ทำอีกแบบ" นั่นคือสัญญาณเตือนว่าโค้ดของคุณเริ่มมีปัญหาแล้ว เพราะเมื่อไหร่ที่มีประเภทข้อมูลใหม่เข้ามา คุณจะต้องกลับมาแก้ไขโค้ดเดิมซ้ำๆ จนเสี่ยงที่จะเกิดบั๊ก (ข้อผิดพลาดในโปรแกรม) ได้ง่าย

การใช้ Composite จะทำให้คุณตัดปัญหาการเช็คประเภทข้อมูลทิ้งไปได้เลย คุณแค่สั่งงานผ่านตัวกลางเดียว ทุกอย่างในระบบจะจัดการตัวเองได้ตามหน้าที่ของมัน ไม่ว่าจะเป็นชิ้นงานเล็กหรือกลุ่มก้อนขนาดใหญ่

สร้างโครงสร้างพื้นฐานด้วย Interface

หัวใจสำคัญคือการสร้างตัวกลางที่ทุกอย่างต้องทำตาม ขั้นตอนนี้สำคัญมาก เพราะมันจะเป็นสัญญาว่าไม่ว่าจะเป็นของเดี่ยวหรือกลุ่ม ต้องมีฟังก์ชันการทำงานที่ชื่อเหมือนกันเป๊ะ

เราจะเริ่มจากการประกาศ Abstract Class (คลาสแม่ที่ทำหน้าที่กำหนดโครงสร้างแต่ยังไม่ใส่รายละเอียด) หรือ Interface เพื่อกำหนดวิธีการทำงาน เช่น ถ้าเป็นระบบตะกร้าสินค้า ก็ต้องมีฟังก์ชันคำนวณราคา

มือใหม่มักพลาดตรงที่ลืมกำหนดให้ทั้งของเดี่ยวและกลุ่มสืบทอดมาจากคลาสเดียวกัน ทำให้ตอนใช้งานจริงต้องมานั่งแยกประเภทข้อมูลอีกรอบ ซึ่งจะทำให้แพทเทิร์นนี้ไม่ทำงานตามความตั้งใจครับ

// ส่วนของ Interface ที่ทุกคนต้องทำตาม
abstract class PriceComponent {
  // ฟังก์ชันคำนวณราคาที่ทุกคลาสต้องมี
  double getPrice();
}

บรรทัดที่ 2 คือการประกาศคลาสแม่ และบรรทัดที่ 4 คือฟังก์ชัน getPrice() ซึ่งเป็นสัญญาว่าใครที่ดึงคลาสนี้ไปใช้ ต้องเขียนวิธีการคำนวณราคาเองครับ ผลลัพธ์คือเราได้มาตรฐานกลางที่ทุกส่วนในระบบต้องปฏิบัติตาม

แยกชิ้นงานเดี่ยวและกลุ่มออกจากกัน

หลังจากมีสัญญาแล้ว เราจะสร้าง Leaf (วัตถุชิ้นสุดท้ายที่ไม่มีลูก) และ Composite (วัตถุที่บรรจุวัตถุอื่นไว้ข้างใน) ขึ้นมา โดย Leaf จะทำหน้าที่คำนวณราคาของตัวเองตรงๆ ส่วน Composite จะทำหน้าที่รวมราคาจากลูกๆ ของมัน

ข้อดีของวิธีนี้คือ Composite ไม่จำเป็นต้องรู้ว่าลูกของมันเป็นสินค้าเดี่ยวหรือเป็นชุดเซตอื่น มันรู้แค่ว่าลูกทุกคนสามารถตอบคำถาม getPrice() ได้เหมือนกันหมด ทำให้ระบบมีความยืดหยุ่นสูงมาก

จุดที่ต้องระวังคือการจัดการ Collection (กลุ่มข้อมูล) ในคลาส Composite อย่าลืมเขียนฟังก์ชันเพิ่มหรือลบสมาชิกให้เรียบร้อย เพื่อให้เราสามารถจัดกลุ่มข้อมูลได้ตามต้องการในภายหลังครับ

// สินค้าชิ้นเดียว (Leaf)
class CartItem extends PriceComponent {
  final double price;
  CartItem(this.price);
  @override
  double getPrice() => price;
}

// กลุ่มสินค้า (Composite)
class ItemBundle extends PriceComponent {
  final List<PriceComponent> _items = [];
  void add(PriceComponent item) => _items.add(item);
  @override
  double getPrice() {
    // รวมราคาสินค้าทุกชิ้นในกลุ่ม
    return _items.fold(0, (sum, item) => sum + item.getPrice());
  }
}

ในคลาส CartItem เราแค่ส่งราคาคืนไปตรงๆ ส่วน ItemBundle เราใช้ fold (ฟังก์ชันรวมค่าในรายการ) เพื่อบวกราคาจากทุกคนที่อยู่ในลิสต์ ผลลัพธ์คือเราสามารถคำนวณราคาสินค้าเป็นชิ้น หรือราคาทั้งตะกร้าได้ด้วยคำสั่งเดียว

ตัวอย่างการนำไปใช้งานจริง

เมื่อทุกอย่างพร้อม เราก็สามารถนำมาประกอบกันเป็นโครงสร้างที่ซับซ้อนได้ง่ายๆ คุณสามารถใส่สินค้าลงในชุดเซต และใส่ชุดเซตลงในตะกร้าใหญ่ได้โดยไม่มีปัญหา เพราะทุกอย่างมองว่าเป็น PriceComponent เหมือนกันหมด

การเขียนแบบนี้ช่วยให้ Main Code (โค้ดหลักที่สั่งรันโปรแกรม) ไม่ต้องรับรู้รายละเอียดภายในเลย มันแค่สั่งให้ตะกร้าคำนวณราคา แล้วตะกร้าก็จะไปไล่เรียกราคาจากสินค้าทุกชิ้นเองโดยอัตโนมัติ

มือใหม่มักจะลืมเช็คว่าถ้าลิสต์ว่างเปล่าจะเกิดอะไรขึ้น อย่าลืมเขียนโค้ดรองรับกรณีที่ไม่มีสินค้าในตะกร้าไว้ด้วยนะครับ เพื่อป้องกันโปรแกรมพัง (Crash) ระหว่างรัน

void main() {
  final burger = CartItem(100);
  final drink = CartItem(50);
  final combo = ItemBundle();
  combo.add(burger);
  combo.add(drink);
  // สั่งคำนวณราคาชุดเซต
  print('ราคาชุดเซต: ${combo.getPrice()}');
}

โค้ดนี้สร้างสินค้า 2 ชิ้นแล้วเอาไปรวมใน combo จากนั้นสั่ง combo.getPrice() ผลลัพธ์ที่ได้คือ 150.0 ซึ่งเกิดจากการรวมราคา 100 + 50 โดยอัตโนมัติครับ

บทสรุปและการนำไปใช้

Composite Design Pattern คือเครื่องมือทรงพลังที่เปลี่ยนงานยากให้กลายเป็นเรื่องง่าย โดยการมองกลุ่มข้อมูลและข้อมูลเดี่ยวให้เป็นมาตรฐานเดียวกัน สิ่งนี้ช่วยให้โปรแกรมของคุณขยายตัวได้โดยไม่ต้องแก้ไขโค้ดเดิมบ่อยๆ

ลองนำไปปรับใช้กับโปรเจกต์ที่กำลังทำอยู่ดูครับ เช่น ถ้าคุณกำลังทำแอปจัดการรายรับรายจ่าย ลองใช้แพทเทิร์นนี้จัดการหมวดหมู่ค่าใช้จ่ายที่มีรายการย่อยๆ อยู่ข้างในดู มันจะช่วยให้การสรุปยอดรายเดือนกลายเป็นเรื่องง่ายเพียงแค่เรียกฟังก์ชันเดียว

การฝึกฝนใช้ Design Pattern ไม่ใช่แค่การจำรูปแบบ แต่คือการเข้าใจว่า "ทำไม" เราถึงต้องออกแบบโค้ดแบบนี้ หากคุณฝึกทำตามนี้บ่อยๆ คุณจะเริ่มมองเห็นโครงสร้างของปัญหาได้ชัดเจนขึ้น และก้าวเข้าสู่การเป็นโปรแกรมเมอร์ที่เขียนโค้ดได้มืออาชีพมากขึ้นแน่นอนครับ


ที่มา: The Composite Design Pattern: How to Work with Individual Objects and Groups Through the Same Interface — freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

แชร์บทความ

Facebook X LINE

บทความที่เกี่ยวข้อง

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

เบื่อไหมที่ต้องสลับหน้าจอไปมาเพื่อจัดการเซิร์ฟเวอร์? มาลองใช้ Easy SSH ปลั๊กอิน VS Code ที่ช่วยให้คุณรีโมทผ่าน Terminal ได้สะดวก แถมโหลดไฟล์ได้ง่ายแค่กด Ctrl+click

ที่มา: DEV Community

1 hour ago 11 นาที
2 views
วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

อยากทำแอปท่องเที่ยวแต่ดึงข้อมูลราคาจาก Google Hotels ไม่ได้? มาดูวิธีใช้ Apify Actor ช่วยดึงข้อมูลแบบอัตโนมัติด้วย Python ง่ายๆ ไม่ต้องกลัวเว็บพัง

ที่มา: DEV Community

5 hours ago 8 นาที
3 views
วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

เคยไหม? โค้ดรันได้ในเครื่องแต่พอปล่อยจริงกลับพัง! มาดูวิธีตรวจสอบความพร้อมของโปรเจกต์ก่อนอัปขึ้น GitHub ด้วยเครื่องมือ ReleaseReady กัน

ที่มา: DEV Community

9 hours ago 9 นาที
4 views