รู้จักกับ Design Pattern - Creational (Part 1/3)
/ 17 min read
สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video
Design Pattern คืออะไร ?
Design Pattern คือแนวคิดหรือแบบแผนที่สร้างขึ้นเพื่อแก้ไขปัญหาที่เกิดขึ้นในการออกแบบและพัฒนาซอฟต์แวร์ โดยมีวัตถุประสงค์ในการเพิ่มความยืดหยุ่นในการจัดการกับปัญหาต่างๆ ที่พบในการพัฒนา software และช่วยให้สามารถนำไปใช้ในสถานการณ์ที่คล้ายคลึงกันได้
เพื่อให้ง่ายต่อการเรียนรู้ บทความชุดนี้ (รวมถึงใน Video) จะแบ่งออกเป็นทั้งหมด 3 ตอนคือ
- Creational Pattern (รูปแบบการสร้างวัตถุ) (** ซึ่งก็คือตอนที่กำลังอ่านอยู่นี้) เป้าหมายของ Creational Pattern เป็นการสร้างวัตถุ (Object) อย่างมีระเบียบเรียบร้อย ตัวอย่างเช่น Singleton, Factory, Builder
- Structural Pattern (รูปแบบการสร้างโครงสร้าง) เน้นการสร้างโครงสร้างให้กับ Object ซึ่งช่วยให้ Object แต่ละตัวสามารถทำงานร่วมกันได้อย่างมีประสิทธิภาพ ตัวอย่างเช่น Adapter, Decorator, Composite
- Behavioral Pattern (รูปแบบการทำงาน) เน้นการจัดการและควบคุมกระบวนการทำงานของ Object ตัวอย่างเช่น Observer, Strategy, Command
ซึ่ง Series สั้น 3 ตอนนี้ เราจะมาลงลึกทีละตัวในแต่ละหมวดหมู่กัน สำหรับใครยังไม่ได้ดูเรื่อง OOP สามารถย้อนดูก่อนได้ ที่นี่ (แนะนำให้ดูก่อน เพราะในหัวข้อนี้เราจะไม่ย้ำเรื่องคุณสมบัติของ OOP กันนะครับ)
Design pattern Creational คืออะไร ?
Design pattern Creational คือแนวคิดที่ใช้การสร้างวัตถุ (Object) อย่างมีระเบียบเรียบร้อย เพื่อเพิ่มความยืดหยุ่นในการจัดการกับปัญหาต่างๆ ที่พบในการพัฒนา software โดยรูปแบบที่นิยมใช้ที่จะขอนำเสนอในบทความนี้ มีทั้งหมด 5 แบบคือ
- Singleton รูปแบบที่รับประกันว่ามีเพียง instance เดียวของ class เท่านั้นที่ถูกสร้างในระบบ
- Factory Method รูปแบบที่กำหนด interface สำหรับการสร้าง object และให้ class ย่อยเป็นผู้กำหนดประเภทของ Object ที่จะสร้าง
- Abstract Factory รูปแบบที่ขยายแนวคิดของ Factory Method โดยสร้างกลุ่มของ object ที่เกี่ยวข้องกันให้ใช้งานร่วมกันได้
- Builder รูปแบบที่ใช้สร้าง object ซึ่งช่วยให้สามารถสร้าง object ที่ซับซ้อนและมีค่าตั้งต้นที่หลากหลายได้โดยไม่ต้องสร้าง class ใหม่หรือแก้ไข code ของ class เดิมได้
- Prototype รูปแบบที่ช่วยสร้าง Object ใหม่โดยการคัดลอก Object ที่มีอยู่ได้
เช่นเดียวกับหัวข้อ OOP เราจะสื่อสารผ่านภาษา Typescript เนื่องจากเป็นภาษาที่ใช้งานเป็นจำนวนมากอีกหนึ่งภาษา และสื่อสารง่ายที่สุด เรามารู้จัก Design Pattern Creational แต่ละแบบกัน
1. รูปแบบ Singleton
องค์ประกอบ Singleton
รูปแบบ Singleton รับประกันว่ามีเพียง instance เดียวของ class เท่านั้นในระบบ รูปแบบนี้มักใช้สำหรับ class ที่ต้องการจัดการทรัพยากรระบบทั่วไป หรือ class ที่ต้องการให้แน่ใจว่ามีสถานะที่สอดคล้องกันตลอดทั้งระบบ (เกิดขึ้นเพียงครั้งเดียวในระบบเท่านั้น)
นี่คือ code ตัวอย่างของ Singleton
code ด้านบนเป็นตัวอย่างของรูปแบบ Singleton ที่ใช้สร้าง Logger ซึ่งเป็น Class ที่ใช้สำหรับบันทึกข้อความลง Log Console โดยมีเงื่อนไขว่าจะต้องมี instance เดียวของ Logger เท่านั้นในระบบ
- ใน class Logger นั้นมีตัวแปร
instance
ที่เป็น class ซึ่งจะใช้เก็บ instance ของ Logger ที่ถูกสร้างขึ้น - ใน constructor ของ Logger จะตรวจสอบว่ามี instance ของ Logger อยู่แล้วหรือไม่ ถ้ามีอยู่แล้วจะส่งค่า Error ออกไป (เพื่อไม่ให้ instance ซ้ำได้) ในกรณีที่ยังไม่มี instance ของ Logger ก็จะกำหนดค่า
Logger.instance
เป็น instance ปัจจุบัน - มี method
log(message)
ที่ใช้สำหรับบันทึกข้อความลง Console - method
getInstance()
ใช้สำหรับการเข้าถึง instance ของ Logger โดยตรวจสอบว่ามี instance อยู่หรือไม่ ถ้ายังไม่มีก็จะสร้าง instance ขึ้นใหม่และกำหนดให้Logger.instance
เป็น instance ใหม่นั้น แล้วคืนค่า instance กลับให้กับผู้เรียกใช้งาน
ตัวอย่าง use case การใช้งาน Singleton ใน web application
ใน web application มีสถานการณ์ที่ต้องการให้มี instance เดียวของ Class ในระบบ เช่น การใช้งาน Class ที่จัดการกับการเชื่อมต่อฐานข้อมูล หรือ Class ที่รับผิดชอบในการจัดการกับการกำหนดค่าระบบ
ตัวอย่างเช่น ใน web application ที่มีการเชื่อมต่อฐานข้อมูล การเขียน Class ที่ใช้ในการเชื่อมต่อฐานข้อมูลแบบ Singleton จะมีประโยชน์ เพราะจะเก็บเพียง instance เดียวของ Class นี้เท่านั้น และทุกส่วนของระบบที่ต้องการใช้งาน Class นี้สามารถเรียกใช้งานได้จาก instance เดียวกัน ตาม code นี้
ในตัวอย่างข้างต้น เราสร้าง class DatabaseConnection
ที่ใช้สำหรับเชื่อมต่อกับฐานข้อมูล โดยใช้รูปแบบ Singleton ซึ่งมีขั้นตอนการสร้าง instance สองขั้นตอนคือ
- ใน constructor ของ class
DatabaseConnection
เราตรวจสอบว่ามี instance ของ class นี้อยู่แล้วหรือไม่ ถ้ามีอยู่แล้วจะส่งค่าข้อผิดพลาด (Error) ออกไป และหากยังไม่มี instance ของ class นี้เราจะกำหนดค่าDatabaseConnection.instance
เป็น instance ปัจจุบัน - เราสร้าง method
getInstance()
ที่ใช้สำหรับการเข้าถึง instance ของ classDatabaseConnection
โดยตรวจสอบว่ามี instance อยู่หรือไม่ ถ้ายังไม่มีก็จะสร้าง instance ขึ้นใหม่และกำหนดให้DatabaseConnection.instance
เป็น instance ใหม่นั้น แล้วคืนค่า instance กลับให้กับผู้เรียกใช้งาน
ด้วยรูปแบบ Singleton ที่ใช้งานใน web application เราสามารถแน่ใจได้ว่าการเชื่อมต่อกับฐานข้อมูลจะมี instance เดียวที่ถูกสร้างขึ้นและใช้งานร่วมกันในระบบเท่านั้น
นอกจากการใช้ Singleton เพื่อการเชื่อมต่อฐานข้อมูลที่อธิบายไว้ในตัวอย่าง ยังมี use case อื่นๆ ที่สามารถใช้ Singleton ได้ดังนี้:
- Configuration Settings: การใช้ Singleton เพื่อสร้าง instance ของ class ConfigManager เพื่อจัดการกับการตั้งค่าระบบที่ใช้ในแอปพลิเคชัน
- Caching: การใช้ Singleton เพื่อสร้าง instance ของ class CacheManager เพื่อจัดการกับการเก็บแคชข้อมูลเพื่อเพิ่มประสิทธิภาพในการเรียกใช้งานข้อมูล
- Connection Pool: การใช้ Singleton เพื่อสร้าง instance ของ class ConnectionPool เพื่อจัดการกับการเชื่อมต่อกับฐานข้อมูลเป็นจำนวนมาก (ทำให้การเชื่อมต่อสามารถเกิดขึ้นเพียงจุดเดียวใน code ได้)
2. รูปแบบ Factory Method
องค์ประกอบ Factory Method
รูปแบบ Factory Method กำหนด interface สำหรับการสร้าง Object แต่ให้ Class ย่อยเป็นผู้กำหนดประเภทของ Object ที่จะสร้าง รูปแบบนี้ช่วยเพิ่มความยืดหยุ่นให้กับระบบ ช่วยให้สามารถเปลี่ยนแปลงวิธีการสร้าง Object ได้โดยไม่ต้องแก้ไข code ของ Class ที่นำไปใช้งาน
รูปแบบ Factory Method ประกอบด้วยองค์ประกอบต่อไปนี้
- Product Interface เป็น interface ที่กำหนด method ที่ต้องมีใน Object ที่ถูกสร้างขึ้นโดย Factory Method (โดยปกติของ OOP จะใช้เป็น abstract class)
- Concrete Products เป็น Class ที่สืบทอดมาจาก Product Interface และมีการเขียน method ที่ถูกกำหนดใน Product Interface
- Creator Class เป็น Class ที่มี method สร้าง Object (Factory Method) ซึ่งใช้สร้าง Object ตามประเภทที่ต้องการ
เรามาลองดูตัวอย่างนี้กัน
Code ด้านบนเป็นตัวอย่างของ Factory Method ที่ใช้ในการสร้าง Object ของ Vehicle โดย
- มี Vehicle เป็น Abstract Product Class และ มี Car และ Bike เป็น Concrete Products ที่สืบทอดมาจาก Vehicle Class
- ใน VehicleFactory Class จะมี method
createVehicle(type)
ที่ใช้สร้าง Object ตามประเภทของ Vehicle ที่ต้องการ โดยใช้ switch case ในการสร้าง Object (แยกประเภท) และส่งคืน Object กลับไปยังผู้เรียกใช้งาน - ในส่วนของการใช้งาน เราจะสร้าง Instance ของ VehicleFactory และใช้
createVehicle(type)
เพื่อสร้าง Vehicle ตามประเภทที่ต้องการ เช่นcreateVehicle('car')
จะสร้าง Object Car และcreateVehicle('bike')
จะสร้าง Object Bike - เมื่อได้ Object แล้วสามารถเรียกใช้งาน method
start()
ของ Object ได้ เช่น car.start() จะเรียกใช้งาน method start() ของ Car Object และ bike.start() จะเรียกใช้งาน method start() ของ Bike Object
ตัวอย่าง use case ที่ใช้งาน Factory ใน Web application
สมมุติ เรากำลังสร้าง e-commerce ที่ต้องการส่งการแจ้งเตือนให้ผู้ใช้ทราบเกี่ยวกับกิจกรรมต่างๆ เช่น การยืนยันคำสั่งซื้อ สินค้าหมด และโปรโมชันพิเศษ
- มีสามวิธีหลักในการส่งการแจ้งเตือน Email, SMS และ Notification
- แม้ว่าวิธีการส่งจะแตกต่างกัน แต่การแจ้งเตือนทั้งหมดจะมีโครงสร้างพื้นฐานคล้ายกันเช่น
- หัวข้อ บอกหัวข้อหลักของการแจ้งเตือน เช่น “ยืนยันการสั่งซื้อของคุณ”
- เนื้อหา อธิบายรายละเอียดของการแจ้งเตือน
- ปุ่มดำเนินการ ให้ลิงก์หรือปุ่มเพื่อให้ผู้ใช้ดำเนินการต่อ เช่น ไปชำระเงิน ดูสถานะสินค้า
เมื่อนำไอเดีย Notification นี้มาประยุกต์ใช้กับ Factory ก็จะได้หน้าตาประมาณนี้ออกมา
ใน code ด้านบนเป็นตัวอย่างของรูปแบบ Factory Method ที่ใช้ในการสร้าง Object ของการแจ้งเตือน (Notification) โดย
- มี Notification เป็น Abstract Product Class และ มี EmailNotification, SMSNotification, และ PushNotification เป็น Concrete Products ที่สืบทอดมาจาก Notification Class
- ใน NotificationFactory Class จะมี method
createNotification(type)
ที่ใช้สร้าง Object ตามประเภทของ Notification ที่ต้องการ โดยใช้ switch case ในการสร้าง Object และส่งคืน Object กลับไปยังผู้เรียกใช้งาน - ในส่วนของการใช้งาน เราจะสร้าง Instance ของ NotificationFactory และใช้
createNotification(type)
เพื่อสร้าง Notification ตามประเภทที่ต้องการ เช่นcreateNotification('email')
จะสร้าง Object EmailNotification และcreateNotification('sms')
จะสร้าง Object SMSNotification - เมื่อได้ Object แล้วสามารถเรียกใช้งาน method
send(message)
ของ Object ได้ เช่นemail.send('Your order has been shipped!')
จะเรียกใช้งาน methodsend()
ของ EmailNotification Object และsms.send('Your package will arrive today')
จะเรียกใช้งาน methodsend()
ของ SMSNotification Object
นอกจาก use case นี้ ยังมี use case อื่นๆ ที่สามารถใช้รูปแบบ Factory Pattern ได้ เช่น
- Logistics Management ใน application แนว Logistic เราอาจจะสามารถสร้างการขนส่งที่แตกต่างกันเช่น รถบรรทุก, เรือ, เครื่องบิน โดย Factory Method สามารถช่วยรวมประเภท Vehical object เข้าด้วยกัน โดย based จาก logistic requirements ได้ ทำให้ทุกการขนส่งมี method ที่เหมือนๆกันออกมาได้
- Cross-Platform UI Components ใน UI ของแต่ละ OS (Windows, MacOS, Linux) อาจจะมีการแสดงผลที่แตกต่างกัน เราก็สามารถใช้ Factory Method ****สำหรับการสร้าง UI component แยกแต่ละ platform ออกมาได้ แต่ยังคงมี method เหมือนๆกันได้
3. รูปแบบ Abstract Factory
องค์ประกอบ Abstract Factory
รูปแบบ Abstract Factory ขยายแนวคิดของ Factory Method ไปอีกขั้น ช่วยให้สามารถสร้างกลุ่มของ Object ที่เกี่ยวข้องกันโดยไม่ต้องระบุ Class ที่เป็นรูปธรรม (ไม่ต้องกำหนดว่า Class ทำงานยังไง) ก็สามารถสร้างเป็นกลุ่มสำหรับการใช้งานร่วมกันได้ รูปแบบนี้เหมาะสำหรับการสร้างระบบที่มี Object ที่เกี่ยวข้องกันจำนวนมาก (คือ ไม่เหมือนกัน แต่ผลลัพธ์ปลายทางการใช้งานเหมือนกัน)
ความแตกต่างระหว่าง Abstract Factory และ Factory Method อยู่ที่ “ระดับของความสัมพันธ์” ในการสร้าง Object
- Factory Method เป็นรูปแบบที่ใช้สร้าง Object ของ Class เดียว โดยมี method ใน Class เดียวเป็นตัวกำหนดว่าจะสร้าง Object ประเภทใดออกมาใน Factory นั้น (ดังตัวอย่างที่เราเห็นในก่อนหน้านี้)
- Abstract Factory เป็นรูปแบบที่ใช้สร้าง Object จากกลุ่มของ Class ที่เกี่ยวข้องกัน โดยมี method ที่ถูกกำหนดใน interface เพื่อสร้าง Object ในกลุ่มออกมา (เทียบได้กับการสร้างหลายๆ Factory มารวมไว้ Factory เดียวกัน แต่ Factory หลายๆ Factory นั้นยังเป็นประเภทเดียวกันอยู่ แต่สามารถสร้างจาก Abstract Factory ตัวเดียวออกมาได้)
รูปแบบ Abstract Factory เหมาะสำหรับการสร้างระบบที่ต้องการสร้างกลุ่มของ Object ที่เกี่ยวข้องกัน และมีความยืดหยุ่นในการเปลี่ยนแปลง Class ที่ใช้ในการสร้าง Object ได้
รูปแบบ Abstract Factory ประกอบด้วยองค์ประกอบต่อไปนี้
- Abstract Factory เป็น interface ที่กำหนดวิธีสร้าง Object ในกลุ่มที่เกี่ยวข้องกันโดยใช้ method ตามประเภทของ Object
- Concrete Factory เป็น Class ที่สืบทอดมาจาก Abstract Factory และมีการสร้าง Object ในกลุ่มที่เกี่ยวข้อง
- Abstract Product เป็น interface ที่กำหนดวิธีการใช้งานของ Object ในกลุ่มที่เกี่ยวข้อง
- Concrete Product เป็น Class ที่สืบทอดมาจาก Abstract Product และมีการกำหนดวิธีการใช้งานของ Object ในกลุ่มที่เกี่ยวข้อง
เรามาลองดูตัวอย่างกัน
จาก code ด้านบนเป็นตัวอย่างของรูปแบบ Abstract Factory ที่ใช้ในการสร้าง UI Element ในรูปแบบที่เกี่ยวข้องกับ Theme (หรือรูปแบบการแสดงผล)
- ในส่วนของ Abstract Products จะประกอบไปด้วย abstract class
Button
และCheckbox
ที่กำหนด methodrender()
ซึ่งจะถูกสืบทอดไปยัง Concrete Products - Concrete Products ประกอบไปด้วย
DarkButton
,LightButton
,DarkCheckbox
, และLightCheckbox
ที่สืบทอดมาจาก Abstract Products และมีการกำหนด methodrender()
โดยแต่ละ Concrete Product จะทำงานตามรูปแบบที่เกี่ยวข้องกับ Theme ที่กำหนด - Abstract Factory Interface
IUIElementFactory
กำหนด methodcreateButton()
และcreateCheckbox()
ซึ่งจะถูกสืบทอดไปยัง Abstract Factory - Abstract Factory
UIElementFactory
เป็น abstract class ที่สืบทอดมาจากIUIElementFactory
และมีการกำหนด methodcreateButton()
และcreateCheckbox()
ที่ต้องถูก Concrete Factory สืบทอดและกำหนดวิธีการสร้าง Object ที่เกี่ยวข้อง - Concrete Factories
DarkThemeFactory
และLightThemeFactory
สืบทอดมาจาก Abstract FactoryUIElementFactory
และมีการกำหนด methodcreateButton()
และcreateCheckbox()
โดยแต่ละ Concrete Factory จะสร้าง Object ที่เกี่ยวข้องกับ Theme ที่กำหนด
ในส่วนของการใช้งาน
- Function
application(factory: IUIElementFactory)
รับ parameter เป็น instance ของ Abstract Factory และใช้ methodcreateButton()
และcreateCheckbox()
เพื่อสร้าง UI Element และเรียกใช้ methodrender()
ของแต่ละ Object - โดยเมื่อสร้าง dark-themed UI จะใช้ Concrete Factory
DarkThemeFactory
และเรียกใช้งานapplication(darkFactory)
ซึ่งจะสร้าง DarkButton และ DarkCheckbox - และเมื่อสร้าง light-themed UI จะใช้ Concrete Factory
LightThemeFactory
และเรียกใช้งานapplication(lightFactory)
ซึ่งจะสร้าง LightButton และ LightCheckbox
สังเกตจังหวะใช้งาน จะเห็นว่า แค่ทำตาม Interface ที่กำหนดเอาไว้ ก็สามารถใช้งานร่วมกันใน Abstract Factory เดียวกันได้แล้ว (เนื่องจากใช้ข้อตกลงเดียวกันมา) ดังนั้นวิธีนี้จึงอำนวยความสะดวกในการจัดการของที่เหมือนๆกัน แต่มี Setting บางอย่างทำให้ของแสดงผลออกมาแตกต่างกันได้ (เหมือนเช่นเคสนี้)
ตัวอย่าง use case ที่ใช้งาน Abstract ใน Web application
ลองจินตนาการว่าเรากำลังสร้าง web application รองรับหลายภาษา ที่มีฟีเจอร์ต่าง ๆ อย่างเช่น การจัดการผู้ใช้งาน และการจัดการเนื้อหา web application จำเป็นต้องแสดงข้อความและจัดการข้อมูลในภาษาต่าง ๆ ตามภาษาหลักของผู้ใช้งาน
- web application นี้รองรับหลายภาษา เช่น ภาษาไทย อังกฤษ จีน ฯลฯ
- ภาษาหลักของผู้ใช้งาน ระบบจะตรวจสอบว่าผู้ใช้งานแต่ละคนใช้ภาษาอะไรเป็นหลัก โดยอาจจะดูจากตั้งค่าในบัญชีหรือการตั้งค่า Browser ของผู้ใช้งาน (ในเคสตัวอย่างนี้ เราจะสมมุติว่าเรารู้ก่อนนะครับ)
- การแสดงข้อความ web application จะแสดงข้อความต่าง ๆ เช่น ปุ่ม เมนู ข้อความแจ้งเตือน ฯลฯ ในภาษาหลักของผู้ใช้งาน
- การจัดการข้อมูล ข้อมูลต่าง ๆ ภายใน web application เช่น ชื่อผู้ใช้งาน คำอธิบายสินค้า บทความ ฯลฯ จะถูกจัดเก็บและแสดงในภาษาหลักของผู้ใช้งาน
ตัวอย่างเช่น
- ผู้ใช้งานตั้งค่าภาษาหลักเป็นภาษาไทย web application จะแสดงปุ่ม “เข้าสู่ระบบ” แทน “Login” และแสดงรายละเอียดสินค้าเป็นภาษาไทย
- ผู้ใช้งานตั้งค่าภาษาหลักเป็นภาษาอังกฤษ web application จะแสดงปุ่ม “Login” แทน “เข้าสู่ระบบ” และแสดงรายละเอียดสินค้าเป็นภาษาอังกฤษ
เมื่อลองประยุกต์ใช้กับ Abstract Pattern ก็จะเป็นประมาณนี้ออกมา
Code ด้านบนเป็นตัวอย่างการใช้งานรูปแบบ Abstract Factory ในการสร้างและจัดการผู้ใช้งานและเนื้อหาใน web application ตามภาษาหลักของผู้ใช้งาน (Locale) โดย
- ในส่วนของ Abstract Products จะประกอบไปด้วย abstract class
UserManagement
และContentManagement
ที่กำหนด methodcreateUser()
และdeleteUser()
หรือcreatePost()
และdeletePost()
ตามลำดับซึ่งจะถูกสืบทอดไปยัง Concrete Products - Concrete Products ประกอบไปด้วย
EnglishUserManagement
,EnglishContentManagement
,SpanishUserManagement
, และSpanishContentManagement
ที่สืบทอดมาจาก Abstract Products และมีการกำหนด method ตามภาษาหลักของผู้ใช้งาน - Abstract Factory Interface
ILocaleFactory
กำหนด methodcreateUserManagement()
และcreateContentManagement()
ซึ่งจะถูกสืบทอดไปยัง Abstract Factory - Abstract Factory
LocaleFactory
เป็น abstract class ที่สืบทอดมาจากILocaleFactory
และมีการกำหนด methodcreateUserManagement()
และcreateContentManagement()
ที่ต้องถูก Concrete Factory สืบทอดและกำหนดวิธีการสร้าง Object ที่เกี่ยวข้อง - Concrete Factories
EnglishLocaleFactory
และSpanishLocaleFactory
สืบทอดมาจาก Abstract FactoryLocaleFactory
และมีการกำหนด methodcreateUserManagement()
และcreateContentManagement()
โดยแต่ละ Concrete Factory จะสร้าง Object ที่เกี่ยวข้องกับภาษาหลักของผู้ใช้งาน
ในส่วนของการใช้งาน
- Function
getFactoryForLocale(locale: Locale)
รับ parameter เป็นภาษาหลักของผู้ใช้งาน (Locale) และใช้ switch case เพื่อสร้างและส่งคืน instance ของ Concrete Factory ที่เกี่ยวข้องกับภาษาหลักของผู้ใช้งาน - ในตัวอย่างตั้งค่า locale เป็น ‘es’ (Spanish) จะสร้าง instance ของ Concrete Factory
SpanishLocaleFactory
จากนั้นใช้createUserManagement()
เพื่อสร้าง ObjectSpanishUserManagement
และใช้createContentManagement()
เพื่อสร้าง ObjectSpanishContentManagement
- เมื่อสร้าง Object แล้วสามารถเรียกใช้งาน method
createUser()
ของuserManagement
และ methodcreatePost()
ของcontentManagement
ได้
use case อื่นๆที่สามารถใช้ Abstract Factory ได้
- Database Access เมื่อระบบจำเป็นต้องรองรับฐานข้อมูลหลากหลายประเภท (เช่น MySQL, PostgreSQL, MongoDB) Abstract factory ทำหน้าที่สร้างการเชื่อมต่อฐานข้อมูลและการสืบค้นข้อมูลกับแต่ละประเภทฐานข้อมูล โดยไม่จำเป็นต้องเปลี่ยนแปลง logic ของ application ในฝั่งของการเรียกใช้ (คำสั่ง connect, query ยังเหมือนเดิมได้ แต่แค่ logic ภายในของแต่ละตัวจะไม่เหมือนกัน)
- E-commerce Platforms จัดการวิธีการชำระเงินที่แตกต่างกัน (บัตรเครดิต, PayPal, โอนเงินผ่านธนาคาร) หรือวิธีจัดส่งสินค้า ซึ่งอาจมีการใช้งานที่แตกต่างกัน Abstract factory จัดการการสร้าง Object ที่เหมาะสมกับแต่ละวิธีได้
- Document Management application ที่จัดการเอกสารหลากหลายรูปแบบ (PDF, DOCX, TXT) แต่ละรูปแบบต้องการการประมวลผลและการแสดงผลที่แตกต่างกัน Abstract factory ช่วยสร้าง Object ที่เหมาะสมกับแต่ละรูปแบบเอกสาร
4. รูปแบบ Builder
องค์ประกอบ Builder Pattern
รูปแบบ Builder เป็นการแยกส่วนการสร้าง Object ที่ซับซ้อนออกจาก instance ช่วยให้สามารถสร้าง Object เดียวกันด้วยวิธีการที่แตกต่างกันได้ pattern นี้จะช่วยทำให้เราสามารถรวมคำสั่งการสร้างและจัดการคำสั่งการสร้างไว้ภายใน object ได้ ส่งผลทำให้ตอนเรียกใช้งานการสร้าง Object นั้น code มีความซับซ้อนน้อยลงจากการห่อหุ้มคำสั่ง build เหล่านี้เอาไว้
รูปแบบ Builder ประกอบด้วยองค์ประกอบต่อไปนี้
- Product เป็น Object ที่ต้องการสร้างโดย Builder และมีการกำหนดวิธีการใช้งานของ Object
- Builder เป็น interface ที่กำหนดวิธีการสร้าง Product แต่ละตัว ซึ่งประกอบด้วย method ต่าง ๆ ที่ใช้ในกระบวนการสร้าง Product ตามลำดับที่กำหนด
- Concrete Builder เป็น Class ที่สืบทอดมาจาก Builder และมีการแลกเปลี่ยนข้อมูลกับ Product ที่กำลังสร้างขึ้น
- Director เป็น Class ที่ใช้ในการควบคุมกระบวนการสร้าง Product โดยใช้ Builder เพื่อสร้าง Product ตามลำดับที่กำหนด
มาลองดูผ่านตัวอย่าง code นี้กัน
Code ด้านบนเป็นตัวอย่างของรูปแบบ Builder Pattern ที่ใช้ในการสร้าง Object โดยตัวอย่างนี้จะแสดงการสร้าง User โดยมีตัว Builder ที่ช่วยในกระบวนการสร้าง Object ขึ้นมา
- ก่อนอื่นเราจะสร้าง Class ของ User ที่มี properties ต่าง ๆ โดยมี constructor ที่รับ parameter เป็น Builder และกำหนดค่าให้กับ properties ต่าง ๆ ตามที่ Builder กำหนด (อย่างเคสนี้ก็จะต้องใส่ name, age, address, phone)
- สร้าง Class ของ Builder ที่มี properties ต่าง ๆ และ method ที่ใช้ในการกำหนดค่า properties แต่ละตัว และ method สุดท้ายคือ method
build()
ที่สร้าง Object ของ User โดยใช้ Builder เป็น parameter (ก็คือthis
ที่หมายถึง instance ของตัวเอง เพื่อนำข้อมูลมาใช้งาน) - สำหรับการเรียกใช้ เราสามารถสร้าง Object ของ User ได้โดยการสร้าง instance ของ Builder และเรียกใช้ method ต่าง ๆ เพื่อกำหนดค่า properties และใช้ method
build()
เพื่อสร้าง Object ของ User
ตัวอย่างข้างบนจะแสดงการสร้าง User โดยกำหนดชื่อและอายุ หรือ ชื่อและที่อยู่ตามที่ต้องการในแต่ละ case ได้ เมื่อสร้าง Object เสร็จสิ้นเราสามารถใช้ method toString()
เพื่อแสดงข้อมูลของ User ที่สร้างขึ้น ภายใน Class ของ User เองได้เช่นกัน
ตัวอย่าง use case ที่ใช้งาน Builder ใน Web application
จินตนาการว่าเรากำลังใช้ Web application วิเคราะห์ธุรกิจที่ให้ผู้ใช้สร้างรายงานต่างๆ ได้เอง โดยปรับค่าได้ละเอียด เช่น ช่วงเวลาของข้อมูล ตัวกรอง และรูปแบบผลลัพธ์ ซึ่งรายงานเหล่านี้มีตัวแปรมากมาย โดยที่ Builder pattern สามารถช่วยจัดระเบียบและลดความยุ่งยากในการสร้างรายงานได้ โดย
- ขั้นตอนการสร้างรายงาน แทนที่จะโยนตัวเลือกทั้งหมดใส่ผู้ใช้ทีเดียว Builder pattern แบ่งการสร้างรายงานเป็นขั้นๆ ผู้ใช้เลือกระดับข้อมูล (เช่น ทั้งหมด วันนี้ เดือนนี้) ก่อนแล้วค่อยเลือกตัวกรอง (เช่น ลูกค้า สินค้า ช่วงราคา) และเลือกรูปแบบการแสดงผล (ตาราง กราฟ แผ่นนำเสนอ) ก่อนกดแสดงผลลัพธ์ออกมาได้
- การสร้าง Object ทีละชั้น Builder pattern สร้าง “Report Object” ทีละชั้น เริ่มด้วยข้อมูลหลัก (ข้อมูลเบื้องต้น) แล้วค่อยๆ ต่อเติมด้วยตัวกรอง รูปแบบผลลัพธ์ และรายละเอียดอื่นๆ (เหมือนเป็นการค่อยๆต่อเลโก้เข้าไปก่อนจะทำการเอาผลลัพธ์จริงๆออกมาใช้)
- ความยืดหยุ่น ผู้ใช้สามารถปรับแต่งได้ละเอียด เลือกเฉพาะขั้นที่ต้องการ ปรับตัวกรอง ย้อนกลับไปเปลี่ยนระดับข้อมูลได้ง่าย โดยไม่ต้องสร้างใหม่ทั้งหมดได้
มาดูตัวอย่างผ่าน code กัน
code ด้านบนเป็นตัวอย่างของรูปแบบ Builder ที่ใช้ในการสร้าง Object Report โดย
- มี Product คือ class Report ที่กำหนดคุณสมบัติต่างๆ และมี method
generate()
เพื่อสร้างรายงาน - มี Builder คือ class ReportBuilder ที่กำหนด method ต่างๆ ในกระบวนการสร้าง Report ตามลำดับที่กำหนด โดยมีการ return ตัวเองเพื่อให้สามารถเชื่อมต่อ method ต่อไปได้
- การใช้งาน สร้าง instance ของ ReportBuilder และใช้ method ต่างๆ เพื่อกำหนดคุณสมบัติของ Report และเมื่อสร้าง Report เสร็จสิ้น เรียกใช้ method
generate()
เพื่อสร้างรายงาน
รูปแบบ Builder ช่วยให้สามารถสร้างวัตถุที่ซับซ้อนได้อย่างยืดหยุ่นขึ้น โดยไม่ต้องปรับเปลี่ยน code ใน Object โดยตรง ได้
นอกเหนือจาก use case นี้ ก็ยังมี use case ที่ยังสามารถใช้ได้เพิ่มเติมเช่น
- สร้าง Document ที่ซับซ้อน ในพวก text editor หรือ document processing นั้น builder pattern สามารถใช้สำหรับการสร้าง document ที่มีความซับซ้อนจากการค่อยๆประกอบของที่แตกต่างกันอย่าง text, images, tables, graphs โดยค่อยๆเพิ่มแบบ step by step เข้าไปได้ จนสามารถประกอบของออกมาได้
- SQL Query Creation: For building complex SQL queries, especially when the number of columns, conditions, and tables involved can vary significantly. A SQL Query Builder can help construct these queries dynamically, adding parts as required.
- SQL Query Creation อีกหนึ่งเคสที่ใช้บ่อยๆได้เช่นเดียวกันคือ การประกอบ SQL query แบบค่อเนื่องกัน โดยเป็นการค่อยๆประกอบ query ได้ตั้งแต่ condition, จำนวน column และ ตาราง และเพื่อประกอบเสร็จ ก็สามารถ build ออกมาเป็น SQL ที่สามารถนำไปใช้งานได้ โดยสามารถยืดหยุ่นตามตัวแปรที่ใส่ไปได้
5. รูปแบบ Prototype
รูปแบบ Prototype เป็นวิธีการออกแบบ Software ที่ช่วยให้สร้าง Object ใหม่โดยการคัดลอก Object ที่มีอยู่ออกมาได้ (ตามชื่อ Prototype นั้นเลย) เพื่อเป็นการประหยัดกระบวนการสร้าง object ที่อาจจะต้องค่อยๆใส่ข้อมูลทีละชุดเข้าไปจากการสร้าง instance ใหม่ เราสามารถใช้ pattern นี้ในการสร้าง object ที่หน้าตาเหมือนกันออกมาได้เลย
รูปแบบ Prototype ประกอบด้วยองค์ประกอบต่อไปนี้
- Prototype เป็น Object ต้นแบบที่ใช้ในการคัดลอกและสร้าง Object ใหม่ มักใช้ในรูปแบบของ instance ที่มีคุณสมบัติเดียวกันกับวัตถุต้นแบบ
- Concrete Prototype เป็น Class ที่สืบทอดมาจาก Prototype และมีการ clone และสร้าง Object ใหม่จากต้นแบบ
- Client เป็น Class หรือ Module ที่ใช้ในการ เรียกใช้งาน Object ต้นแบบและสร้าง Object ใหม่จากต้นแบบ (เป็นคนที่หยิบ Object หลัง Clone ไปใช้)
มาดูตัวอย่างผ่าน code กัน
code ด้านบนเป็นตัวอย่างของรูปแบบ Prototype ที่ใช้ในการสร้าง Object ใหม่โดยการคัดลอกและ clone วัตถุต้นแบบ ใน code นี้ประกอบด้วย
- มี Prototype คือ class UserProfile ที่ใช้ในการคัดลอกและสร้าง Object ใหม่ โดยมี method
clone()
เพื่อคัดลอก Object ต้นแบบ - มี Concrete Prototype คือ class UserProfile ที่สืบทอดมาจาก Prototype และมีการ clone และสร้าง Object ใหม่จากต้นแบบ
- ในการใช้งาน เราสร้าง instance ของ UserProfile ในรูปแบบของ prototypeProfile และใช้ method
clone()
เพื่อคัดลอก Object ต้นแบบเพื่อสร้าง Object ใหม่ในรูปแบบของ newUserProfile เราสามารถเปลี่ยนค่า properties ของ newUserProfile ได้และสร้าง Object ใหม่โดยไม่ต้องสร้างใหม่ทั้งหมดได้
ตัวอย่าง use case ที่ใช้งาน Prototype ใน Web application
ลองจินตนาการ web application ที่ให้ผู้ใช้สร้าง Dashboard ส่วนตัวได้เอง Dashboard เหล่านี้ประกอบด้วย Widget หลาย ๆ แบบ (เช่น กราฟ, ตาราง, News Feed) ผู้ใช้สามารถปรับแต่ง Widget เหล่านี้ได้เองตามต้องการ ตั้งแต่ขนาด สี แหล่งข้อมูล ฯลฯ โดยสิ่งที่ Prototype pattern เข้ามาช่วยจัดการเรื่องนี้ได้คือ
- ต้นแบบ Widget สร้างวัตถุต้นแบบ (prototype) สำหรับแต่ละประเภท Widget เช่น ต้นแบบของกราฟแท่ง ต้นแบบของตาราง มีค่าตั้งต้นของขนาด สี ฯลฯ
- clone เพื่อปรับแต่ง เมื่อผู้ใช้ต้องการเพิ่ม Widget ลงใน Dashboard ระบบจะ clone Object ต้นแบบนั้นมา 1 ชุด จากนั้นจึงนำการปรับแต่งของผู้ใช้ (ขนาด สี แหล่งข้อมูล) ไปใส่ใน Object ที่ clone มาได้
และนี่คือ code เมื่อประยุกต์ใช้ร่วมกับ Prototype
code ด้านบนเป็นตัวอย่างของการใช้รูปแบบ Prototype ในการสร้างและปรับแต่ง Object ประเภท Widget ใน web application ใน code โดย
- มี Prototype คือ class Widget ที่ใช้ในการคัดลอกและสร้าง Object ใหม่ โดยมี method
clone()
เพื่อคัดลอก Object ต้นแบบ และ methodcustomize()
เพื่อปรับแต่ง Object ตามความต้องการของผู้ใช้ - การใช้งาน สร้าง instance ของ Widget ต้นแบบ (prototype) เช่น chartWidgetPrototype และ tableWidgetPrototype และใช้ method
clone()
เพื่อคัดลอกวัตถุต้นแบบเพื่อสร้างวัตถุใหม่ และสามารถใช้ methodcustomize()
เพื่อปรับแต่งวัตถุตามความต้องการของผู้ใช้ - ผู้ใช้สามารถสร้างและปรับแต่ง Widget ตามความต้องการของตนได้อย่างยืดหยุ่น
รูปแบบ Prototype ช่วยให้สามารถสร้างและปรับแต่งวัตถุใหม่ได้อย่างมีประสิทธิภาพและยืดหยุ่นในการใช้งานใน web application
นอกเหนือจาก use case นี้ เรายังสามารถใช้รูปแบบ Prototype pattern ในกรณีต่อไปนี้ได้
- Graphic Editors and Design Tools นักออกแบบกราฟิกมักต้องการสร้างสำเนาของวัตถุซ้ำหลายครั้ง เช่น รูปร่าง, logo, icon เพื่อปรับแต่งเล็กน้อยสำหรับการใช้งานที่แตกต่างกัน Prototype pattern ช่วยให้การสร้างสำเนาเหล่านี้ได้อย่างง่ายดาย
- Game development สร้างศัตรู ต้นไม้ หรือกระสุน หลายอันจากต้นแบบเดียวกัน ศัตรูแต่ละตัวอาจมีพลังชีวิต ตำแหน่งเริ่มต้น ต่างกัน แต่รูปร่างพื้นฐานเหมือนกัน (เป็น use case ที่ผมใช้บ่อยมากๆเหมือนกัน)
- Caching Object เมื่อสร้างวัตถุจาก Class บางประเภทใช้เวลานาน (คำนวณเยอะ) การ clone วัตถุที่มีอยู่แล้วจะสามารถประหยัดเวลาได้มากกว่า เหมาะกับ Object ที่มีการดึงข้อมูลจากภายนอกมาใช้ เช่น ผลลัพธ์จาก database เป็นต้น
สรุป
นี่คือหัวข้อแรกของ Design pattern อย่างที่ทุกคนเห็น Design pattern นั้นจริงๆไม่ใช่ code ชุดใหม่ที่แตกต่างจาก OOP แต่เป็น “ไอเดีย” ในการนำ OOP มาประยุกต์ใช้ และนี่คือ 1 ใน 3 กลุ่ม Pattern หลักของ Design pattern ในตอนต่อไป เราจะพูดถึง Design pattern structural ซึ่งเป็นแนวคิดในการออกแบบ software ที่เน้นการสร้างโครงสร้างและความสัมพันธ์ระหว่างองค์ประกอบของระบบ โดยวัตถุประสงค์หลักคือเพื่อสร้างโครงสร้างที่ยืดหยุ่นและทำให้ระบบง่ายต่อการเปลี่ยนแปลงและการขยายของระบบในอนาคต เดี๋ยวเราจะมาลงลึกกันในหัวข้อถัดไปนะครับ 😁
Reference
https://refactoring.guru/design-patterns/builder
- มาเรียนรู้การทำ Frontend Testing ผ่าน React กันมี Video
แนะนำพื้นฐานการทำ Component Testing สำหรับการทำ Unit testing ฝั่ง Frontend
- ลอง Firebase Data Connectมี Github
มารู้จัก นวัตกรรม SQL จากฝั่ง Firebase ผ่าน Service ตัวใหม่ Firebase Data Connect กัน
- ทำไมถึงต้องใช้ Nuxt ทั้งๆที่มี Vue อยู่แล้ว ?มี Video
มาทำความรู้จัก Nuxt กันว่ามันคืออะไร มีความแตกต่างกับ Vue ยังไงบ้าง และเคสแบบไหนควรใช้ Nuxt บ้าง
- แนะนำ Dynamic programming แบบนิ่มนวลที่สุดมี Video
บทความนี้จะแนะนำเบื้องต้นเกี่ยวกับ Dynamic programming เทคนิคหนึ่งที่ใช้สำหรับแก้ปัญหาที่ ปัญหาย่อยที่ทับซ้อนกัน (overlapping subproblem)