รู้จักกับ Design Pattern - Creational (Part 1/3)

/ 17 min read

Share on social media

design-pattern-creational สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video

Design Pattern คืออะไร ?

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

เพื่อให้ง่ายต่อการเรียนรู้ บทความชุดนี้ (รวมถึงใน Video) จะแบ่งออกเป็นทั้งหมด 3 ตอนคือ

  1. Creational Pattern (รูปแบบการสร้างวัตถุ) (** ซึ่งก็คือตอนที่กำลังอ่านอยู่นี้) เป้าหมายของ Creational Pattern เป็นการสร้างวัตถุ (Object) อย่างมีระเบียบเรียบร้อย ตัวอย่างเช่น Singleton, Factory, Builder
  2. Structural Pattern (รูปแบบการสร้างโครงสร้าง) เน้นการสร้างโครงสร้างให้กับ Object ซึ่งช่วยให้ Object แต่ละตัวสามารถทำงานร่วมกันได้อย่างมีประสิทธิภาพ ตัวอย่างเช่น Adapter, Decorator, Composite
  3. Behavioral Pattern (รูปแบบการทำงาน) เน้นการจัดการและควบคุมกระบวนการทำงานของ Object ตัวอย่างเช่น Observer, Strategy, Command

ซึ่ง Series สั้น 3 ตอนนี้ เราจะมาลงลึกทีละตัวในแต่ละหมวดหมู่กัน สำหรับใครยังไม่ได้ดูเรื่อง OOP สามารถย้อนดูก่อนได้ ที่นี่ (แนะนำให้ดูก่อน เพราะในหัวข้อนี้เราจะไม่ย้ำเรื่องคุณสมบัติของ OOP กันนะครับ)

Design pattern Creational คืออะไร ?

Design pattern Creational คือแนวคิดที่ใช้การสร้างวัตถุ (Object) อย่างมีระเบียบเรียบร้อย เพื่อเพิ่มความยืดหยุ่นในการจัดการกับปัญหาต่างๆ ที่พบในการพัฒนา software โดยรูปแบบที่นิยมใช้ที่จะขอนำเสนอในบทความนี้ มีทั้งหมด 5 แบบคือ

  1. Singleton รูปแบบที่รับประกันว่ามีเพียง instance เดียวของ class เท่านั้นที่ถูกสร้างในระบบ
  2. Factory Method รูปแบบที่กำหนด interface สำหรับการสร้าง object และให้ class ย่อยเป็นผู้กำหนดประเภทของ Object ที่จะสร้าง
  3. Abstract Factory รูปแบบที่ขยายแนวคิดของ Factory Method โดยสร้างกลุ่มของ object ที่เกี่ยวข้องกันให้ใช้งานร่วมกันได้
  4. Builder รูปแบบที่ใช้สร้าง object ซึ่งช่วยให้สามารถสร้าง object ที่ซับซ้อนและมีค่าตั้งต้นที่หลากหลายได้โดยไม่ต้องสร้าง class ใหม่หรือแก้ไข code ของ class เดิมได้
  5. Prototype รูปแบบที่ช่วยสร้าง Object ใหม่โดยการคัดลอก Object ที่มีอยู่ได้

เช่นเดียวกับหัวข้อ OOP เราจะสื่อสารผ่านภาษา Typescript เนื่องจากเป็นภาษาที่ใช้งานเป็นจำนวนมากอีกหนึ่งภาษา และสื่อสารง่ายที่สุด เรามารู้จัก Design Pattern Creational แต่ละแบบกัน

1. รูปแบบ Singleton

องค์ประกอบ Singleton

รูปแบบ Singleton รับประกันว่ามีเพียง instance เดียวของ class เท่านั้นในระบบ รูปแบบนี้มักใช้สำหรับ class ที่ต้องการจัดการทรัพยากรระบบทั่วไป หรือ class ที่ต้องการให้แน่ใจว่ามีสถานะที่สอดคล้องกันตลอดทั้งระบบ (เกิดขึ้นเพียงครั้งเดียวในระบบเท่านั้น)

นี่คือ code ตัวอย่างของ Singleton

class Logger {
private static instance: Logger | null = null;
private constructor() {
if (Logger.instance) {
throw new Error("Cannot create more than one instance of Logger");
}
Logger.instance = this;
}
public log(message: string): void {
console.log(message);
}
public static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
}
// Usage:
const logger = Logger.getInstance();
logger.log("Hello, world!");

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 {
private static instance: DatabaseConnection | null = null;
private constructor() {
if (DatabaseConnection.instance) {
throw new Error("Cannot create more than one instance of DatabaseConnection");
}
// Code for database connection
DatabaseConnection.instance = this;
}
public static getInstance(): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
// Other code related to database connection
}
// Usage
const dbConnection = DatabaseConnection.getInstance();
// dbConnection is an instance of the DatabaseConnection class used for database connections in the system

ในตัวอย่างข้างต้น เราสร้าง class DatabaseConnection ที่ใช้สำหรับเชื่อมต่อกับฐานข้อมูล โดยใช้รูปแบบ Singleton ซึ่งมีขั้นตอนการสร้าง instance สองขั้นตอนคือ

  1. ใน constructor ของ class DatabaseConnection เราตรวจสอบว่ามี instance ของ class นี้อยู่แล้วหรือไม่ ถ้ามีอยู่แล้วจะส่งค่าข้อผิดพลาด (Error) ออกไป และหากยังไม่มี instance ของ class นี้เราจะกำหนดค่า DatabaseConnection.instance เป็น instance ปัจจุบัน
  2. เราสร้าง method getInstance() ที่ใช้สำหรับการเข้าถึง instance ของ class DatabaseConnection โดยตรวจสอบว่ามี 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 ประกอบด้วยองค์ประกอบต่อไปนี้

  1. Product Interface เป็น interface ที่กำหนด method ที่ต้องมีใน Object ที่ถูกสร้างขึ้นโดย Factory Method (โดยปกติของ OOP จะใช้เป็น abstract class)
  2. Concrete Products เป็น Class ที่สืบทอดมาจาก Product Interface และมีการเขียน method ที่ถูกกำหนดใน Product Interface
  3. Creator Class เป็น Class ที่มี method สร้าง Object (Factory Method) ซึ่งใช้สร้าง Object ตามประเภทที่ต้องการ

เรามาลองดูตัวอย่างนี้กัน

// Abstract Product Class
abstract class Vehicle {
abstract start(): string;
}
// Concrete Products
class Car extends Vehicle {
start(): string {
return 'Car engine started';
}
}
class Bike extends Vehicle {
start(): string {
return 'Bike engine started';
}
}
// Vehicle Type
type VehicleType = 'car' | 'bike';
// Creator Class
class VehicleFactory {
createVehicle(type: VehicleType): Vehicle {
switch (type) {
case 'car':
return new Car();
case 'bike':
return new Bike();
default:
throw new Error('Vehicle type not supported');
}
}
}
// Usage
const factory = new VehicleFactory();
const car = factory.createVehicle('car');
console.log(car.start()); // Output: Car engine started
const bike = factory.createVehicle('bike');
console.log(bike.start()); // Output: Bike engine started

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 ก็จะได้หน้าตาประมาณนี้ออกมา

// Abstract Notification Class
abstract class Notification {
abstract send(message: string): string;
}
// Concrete Products
class EmailNotification extends Notification {
send(message: string): string {
return `Email sent with message: ${message}`;
}
}
class SMSNotification extends Notification {
send(message: string): string {
return `SMS sent with message: ${message}`;
}
}
class PushNotification extends Notification {
send(message: string): string {
return `Push Notification sent with message: ${message}`;
}
}
// Notification Type
type NotificationType = 'email' | 'sms' | 'push';
// Creator Class
class NotificationFactory {
createNotification(type: NotificationType): Notification {
switch (type) {
case 'email':
return new EmailNotification();
case 'sms':
return new SMSNotification();
case 'push':
return new PushNotification();
default:
throw new Error('Notification type not supported');
}
}
}
// Usage
const factory = new NotificationFactory();
const email = factory.createNotification('email');
console.log(email.send('Your order has been shipped!')); // Output: Email sent with message: Your order has been shipped!
const sms = factory.createNotification('sms');
console.log(sms.send('Your package will arrive today')); // Output: SMS sent with message: Your package will arrive today
const push = factory.createNotification('push');
console.log(push.send('A new item is in stock')); // Output: Push Notification sent with message: A new item is in stock

ใน 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!') จะเรียกใช้งาน method send() ของ EmailNotification Object และ sms.send('Your package will arrive today') จะเรียกใช้งาน method send() ของ 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 ประกอบด้วยองค์ประกอบต่อไปนี้

  1. Abstract Factory เป็น interface ที่กำหนดวิธีสร้าง Object ในกลุ่มที่เกี่ยวข้องกันโดยใช้ method ตามประเภทของ Object
  2. Concrete Factory เป็น Class ที่สืบทอดมาจาก Abstract Factory และมีการสร้าง Object ในกลุ่มที่เกี่ยวข้อง
  3. Abstract Product เป็น interface ที่กำหนดวิธีการใช้งานของ Object ในกลุ่มที่เกี่ยวข้อง
  4. Concrete Product เป็น Class ที่สืบทอดมาจาก Abstract Product และมีการกำหนดวิธีการใช้งานของ Object ในกลุ่มที่เกี่ยวข้อง

เรามาลองดูตัวอย่างกัน

// Abstract Products
abstract class Button {
abstract render(): void;
}
abstract class Checkbox {
abstract render(): void;
}
// Concrete Products
class DarkButton extends Button {
render(): void {
console.log("Rendering a dark theme button");
}
}
class LightButton extends Button {
render(): void {
console.log("Rendering a light theme button");
}
}
class DarkCheckbox extends Checkbox {
render(): void {
console.log("Rendering a dark theme checkbox");
}
}
class LightCheckbox extends Checkbox {
render(): void {
console.log("Rendering a light theme checkbox");
}
}
// Abstract Factory Interface
interface IUIElementFactory {
createButton(): Button;
createCheckbox(): Checkbox;
}
// Abstract Factory
abstract class UIElementFactory implements IUIElementFactory {
abstract createButton(): Button;
abstract createCheckbox(): Checkbox;
}
// Concrete Factories
class DarkThemeFactory extends UIElementFactory {
createButton(): Button {
return new DarkButton();
}
createCheckbox(): Checkbox {
return new DarkCheckbox();
}
}
class LightThemeFactory extends UIElementFactory {
createButton(): Button {
return new LightButton();
}
createCheckbox(): Checkbox {
return new LightCheckbox();
}
}
// Usage
function application(factory: IUIElementFactory): void {
const button = factory.createButton();
const checkbox = factory.createCheckbox();
button.render();
checkbox.render();
}
// Creating a dark-themed UI
const darkFactory = new DarkThemeFactory();
application(darkFactory); // Outputs: Rendering a dark theme button, Rendering a dark theme checkbox
// Creating a light-themed UI
const lightFactory = new LightThemeFactory();
application(lightFactory); // Outputs: Rendering a light theme button, Rendering a light theme checkbox

จาก code ด้านบนเป็นตัวอย่างของรูปแบบ Abstract Factory ที่ใช้ในการสร้าง UI Element ในรูปแบบที่เกี่ยวข้องกับ Theme (หรือรูปแบบการแสดงผล)

  • ในส่วนของ Abstract Products จะประกอบไปด้วย abstract class Button และ Checkbox ที่กำหนด method render() ซึ่งจะถูกสืบทอดไปยัง Concrete Products
  • Concrete Products ประกอบไปด้วย DarkButton, LightButton, DarkCheckbox, และ LightCheckbox ที่สืบทอดมาจาก Abstract Products และมีการกำหนด method render() โดยแต่ละ Concrete Product จะทำงานตามรูปแบบที่เกี่ยวข้องกับ Theme ที่กำหนด
  • Abstract Factory Interface IUIElementFactory กำหนด method createButton() และ createCheckbox() ซึ่งจะถูกสืบทอดไปยัง Abstract Factory
  • Abstract Factory UIElementFactory เป็น abstract class ที่สืบทอดมาจาก IUIElementFactory และมีการกำหนด method createButton() และ createCheckbox() ที่ต้องถูก Concrete Factory สืบทอดและกำหนดวิธีการสร้าง Object ที่เกี่ยวข้อง
  • Concrete Factories DarkThemeFactory และ LightThemeFactory สืบทอดมาจาก Abstract Factory UIElementFactory และมีการกำหนด method createButton() และ createCheckbox() โดยแต่ละ Concrete Factory จะสร้าง Object ที่เกี่ยวข้องกับ Theme ที่กำหนด

ในส่วนของการใช้งาน

  • Function application(factory: IUIElementFactory) รับ parameter เป็น instance ของ Abstract Factory และใช้ method createButton() และ createCheckbox() เพื่อสร้าง UI Element และเรียกใช้ method render() ของแต่ละ 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 ก็จะเป็นประมาณนี้ออกมา

// Abstract Products
abstract class UserManagement {
abstract createUser(): void;
abstract deleteUser(): void;
}
abstract class ContentManagement {
abstract createPost(): void;
abstract deletePost(): void;
}
// Concrete Products for English
class EnglishUserManagement extends UserManagement {
createUser(): void {
console.log("Creating user in English");
}
deleteUser(): void {
console.log("Deleting user in English");
}
}
class EnglishContentManagement extends ContentManagement {
createPost(): void {
console.log("Creating post in English");
}
deletePost(): void {
console.log("Deleting post in English");
}
}
// Concrete Products for Spanish
class SpanishUserManagement extends UserManagement {
createUser(): void {
console.log("Creando usuario en Español");
}
deleteUser(): void {
console.log("Eliminando usuario en Español");
}
}
class SpanishContentManagement extends ContentManagement {
createPost(): void {
console.log("Creando publicación en Español");
}
deletePost(): void {
console.log("Eliminando publicación en Español");
}
}
// Abstract Factory Interface
interface ILocaleFactory {
createUserManagement(): UserManagement;
createContentManagement(): ContentManagement;
}
// Abstract Factory
abstract class LocaleFactory implements ILocaleFactory {
abstract createUserManagement(): UserManagement;
abstract createContentManagement(): ContentManagement;
}
// Concrete Factories
class EnglishLocaleFactory extends LocaleFactory {
createUserManagement(): UserManagement {
return new EnglishUserManagement();
}
createContentManagement(): ContentManagement {
return new EnglishContentManagement();
}
}
class SpanishLocaleFactory extends LocaleFactory {
createUserManagement(): UserManagement {
return new SpanishUserManagement();
}
createContentManagement(): ContentManagement {
return new SpanishContentManagement();
}
}
// Locale Type
type Locale = "en" | "es";
// Usage based on locale
function getFactoryForLocale(locale: Locale): LocaleFactory {
switch (locale) {
case "en":
return new EnglishLocaleFactory();
case "es":
return new SpanishLocaleFactory();
default:
throw new Error("Locale not supported");
}
}
// Example usage
const factory = getFactoryForLocale("es"); // Spanish
const userManagement = factory.createUserManagement();
userManagement.createUser(); // Output: Creando usuario en Español
const contentManagement = factory.createContentManagement();
contentManagement.createPost(); // Output: Creando publicación en Español

Code ด้านบนเป็นตัวอย่างการใช้งานรูปแบบ Abstract Factory ในการสร้างและจัดการผู้ใช้งานและเนื้อหาใน web application ตามภาษาหลักของผู้ใช้งาน (Locale) โดย

  • ในส่วนของ Abstract Products จะประกอบไปด้วย abstract class UserManagement และ ContentManagement ที่กำหนด method createUser() และ deleteUser() หรือ createPost() และ deletePost() ตามลำดับซึ่งจะถูกสืบทอดไปยัง Concrete Products
  • Concrete Products ประกอบไปด้วย EnglishUserManagement, EnglishContentManagement, SpanishUserManagement, และ SpanishContentManagement ที่สืบทอดมาจาก Abstract Products และมีการกำหนด method ตามภาษาหลักของผู้ใช้งาน
  • Abstract Factory Interface ILocaleFactory กำหนด method createUserManagement() และ createContentManagement() ซึ่งจะถูกสืบทอดไปยัง Abstract Factory
  • Abstract Factory LocaleFactory เป็น abstract class ที่สืบทอดมาจาก ILocaleFactory และมีการกำหนด method createUserManagement() และ createContentManagement() ที่ต้องถูก Concrete Factory สืบทอดและกำหนดวิธีการสร้าง Object ที่เกี่ยวข้อง
  • Concrete Factories EnglishLocaleFactory และ SpanishLocaleFactory สืบทอดมาจาก Abstract Factory LocaleFactory และมีการกำหนด method createUserManagement() และ createContentManagement() โดยแต่ละ Concrete Factory จะสร้าง Object ที่เกี่ยวข้องกับภาษาหลักของผู้ใช้งาน

ในส่วนของการใช้งาน

  • Function getFactoryForLocale(locale: Locale) รับ parameter เป็นภาษาหลักของผู้ใช้งาน (Locale) และใช้ switch case เพื่อสร้างและส่งคืน instance ของ Concrete Factory ที่เกี่ยวข้องกับภาษาหลักของผู้ใช้งาน
  • ในตัวอย่างตั้งค่า locale เป็น ‘es’ (Spanish) จะสร้าง instance ของ Concrete Factory SpanishLocaleFactory จากนั้นใช้ createUserManagement() เพื่อสร้าง Object SpanishUserManagement และใช้ createContentManagement() เพื่อสร้าง Object SpanishContentManagement
  • เมื่อสร้าง Object แล้วสามารถเรียกใช้งาน method createUser() ของ userManagement และ method createPost() ของ 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 ประกอบด้วยองค์ประกอบต่อไปนี้

  1. Product เป็น Object ที่ต้องการสร้างโดย Builder และมีการกำหนดวิธีการใช้งานของ Object
  2. Builder เป็น interface ที่กำหนดวิธีการสร้าง Product แต่ละตัว ซึ่งประกอบด้วย method ต่าง ๆ ที่ใช้ในกระบวนการสร้าง Product ตามลำดับที่กำหนด
  3. Concrete Builder เป็น Class ที่สืบทอดมาจาก Builder และมีการแลกเปลี่ยนข้อมูลกับ Product ที่กำลังสร้างขึ้น
  4. Director เป็น Class ที่ใช้ในการควบคุมกระบวนการสร้าง Product โดยใช้ Builder เพื่อสร้าง Product ตามลำดับที่กำหนด

มาลองดูผ่านตัวอย่าง code นี้กัน

// User Interface
interface IUser {
name: string;
age?: number;
phone?: string;
address?: string;
}
// User Class
class User implements IUser {
name: string;
age?: number;
phone?: string;
address?: string;
constructor(builder: UserBuilder) {
this.name = builder.name;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
toString(): string {
return JSON.stringify(this);
}
}
// UserBuilder Class
class UserBuilder {
name: string;
age?: number;
phone?: string;
address?: string;
constructor(name: string) {
this.name = name;
}
setAge(age: number): UserBuilder {
this.age = age;
return this;
}
setPhone(phone: string): UserBuilder {
this.phone = phone;
return this;
}
setAddress(address: string): UserBuilder {
this.address = address;
return this;
}
build(): User {
return new User(this);
}
}
// Usage
const user1 = new UserBuilder("John Doe").setAge(30).setPhone("1234567890").build();
console.log(user1.toString()); // User with name, age, and phone
const user2 = new UserBuilder("Alice").setAddress("123 Main St").build();
console.log(user2.toString()); // User with name and address

Code ด้านบนเป็นตัวอย่างของรูปแบบ Builder Pattern ที่ใช้ในการสร้าง Object โดยตัวอย่างนี้จะแสดงการสร้าง User โดยมีตัว Builder ที่ช่วยในกระบวนการสร้าง Object ขึ้นมา

  1. ก่อนอื่นเราจะสร้าง Class ของ User ที่มี properties ต่าง ๆ โดยมี constructor ที่รับ parameter เป็น Builder และกำหนดค่าให้กับ properties ต่าง ๆ ตามที่ Builder กำหนด (อย่างเคสนี้ก็จะต้องใส่ name, age, address, phone)
  2. สร้าง Class ของ Builder ที่มี properties ต่าง ๆ และ method ที่ใช้ในการกำหนดค่า properties แต่ละตัว และ method สุดท้ายคือ method build() ที่สร้าง Object ของ User โดยใช้ Builder เป็น parameter (ก็คือ this ที่หมายถึง instance ของตัวเอง เพื่อนำข้อมูลมาใช้งาน)
  3. สำหรับการเรียกใช้ เราสามารถสร้าง 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 สามารถช่วยจัดระเบียบและลดความยุ่งยากในการสร้างรายงานได้ โดย

  1. ขั้นตอนการสร้างรายงาน แทนที่จะโยนตัวเลือกทั้งหมดใส่ผู้ใช้ทีเดียว Builder pattern แบ่งการสร้างรายงานเป็นขั้นๆ ผู้ใช้เลือกระดับข้อมูล (เช่น ทั้งหมด วันนี้ เดือนนี้) ก่อนแล้วค่อยเลือกตัวกรอง (เช่น ลูกค้า สินค้า ช่วงราคา) และเลือกรูปแบบการแสดงผล (ตาราง กราฟ แผ่นนำเสนอ) ก่อนกดแสดงผลลัพธ์ออกมาได้
  2. การสร้าง Object ทีละชั้น Builder pattern สร้าง “Report Object” ทีละชั้น เริ่มด้วยข้อมูลหลัก (ข้อมูลเบื้องต้น) แล้วค่อยๆ ต่อเติมด้วยตัวกรอง รูปแบบผลลัพธ์ และรายละเอียดอื่นๆ (เหมือนเป็นการค่อยๆต่อเลโก้เข้าไปก่อนจะทำการเอาผลลัพธ์จริงๆออกมาใช้)
  3. ความยืดหยุ่น ผู้ใช้สามารถปรับแต่งได้ละเอียด เลือกเฉพาะขั้นที่ต้องการ ปรับตัวกรอง ย้อนกลับไปเปลี่ยนระดับข้อมูลได้ง่าย โดยไม่ต้องสร้างใหม่ทั้งหมดได้

มาดูตัวอย่างผ่าน code กัน

// Report Interface
interface IReport {
dateRange: { from: string; to: string };
filters: string[];
format: string;
}
// Complex Object
class Report implements IReport {
dateRange: { from: string; to: string };
filters: string[];
format: string;
constructor(builder: ReportBuilder) {
this.dateRange = builder.dateRange;
this.filters = builder.filters;
this.format = builder.format;
// other report parameters...
}
generate(): void {
// Logic to generate the report
console.log(`Generating report with: ${JSON.stringify(this)}`);
}
}
// Builder
class ReportBuilder {
dateRange: { from: string; to: string };
filters: string[];
format: string;
setDateRange(dateRange: { from: string; to: string }): ReportBuilder {
this.dateRange = dateRange;
return this;
}
setFilters(filters: string[]): ReportBuilder {
this.filters = filters;
return this;
}
setFormat(format: string): ReportBuilder {
this.format = format;
return this;
}
build(): Report {
return new Report(this);
}
}
// Usage
const reportBuilder = new ReportBuilder();
const report = reportBuilder
.setDateRange({ from: "2021-01-01", to: "2021-12-31" })
.setFilters(["region:Europe", "product:Books"])
.setFormat("PDF")
.build();
report.generate(); // Generating report with specified parameters

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 ประกอบด้วยองค์ประกอบต่อไปนี้

  1. Prototype เป็น Object ต้นแบบที่ใช้ในการคัดลอกและสร้าง Object ใหม่ มักใช้ในรูปแบบของ instance ที่มีคุณสมบัติเดียวกันกับวัตถุต้นแบบ
  2. Concrete Prototype เป็น Class ที่สืบทอดมาจาก Prototype และมีการ clone และสร้าง Object ใหม่จากต้นแบบ
  3. Client เป็น Class หรือ Module ที่ใช้ในการ เรียกใช้งาน Object ต้นแบบและสร้าง Object ใหม่จากต้นแบบ (เป็นคนที่หยิบ Object หลัง Clone ไปใช้)

มาดูตัวอย่างผ่าน code กัน

class UserProfile {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
// Assume there are more complex fields here
}
clone(): UserProfile {
// The simplest approach to cloning, creating a new instance with the same values.
return new UserProfile(this.name, this.age);
}
toString(): string {
return `Name: ${this.name}, Age: ${this.age}`;
}
}
// Usage
const prototypeProfile = new UserProfile("John Doe", 30);
// Cloning the prototype to create a new profile
const newUserProfile = prototypeProfile.clone();
newUserProfile.name = "Jane Doe"; // Changing the name for the new profile
console.log(prototypeProfile.toString()); // Output: Name: John Doe, Age: 30
console.log(newUserProfile.toString()); // Output: Name: Jane Doe, Age: 30

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 เข้ามาช่วยจัดการเรื่องนี้ได้คือ

  1. ต้นแบบ Widget สร้างวัตถุต้นแบบ (prototype) สำหรับแต่ละประเภท Widget เช่น ต้นแบบของกราฟแท่ง ต้นแบบของตาราง มีค่าตั้งต้นของขนาด สี ฯลฯ
  2. clone เพื่อปรับแต่ง เมื่อผู้ใช้ต้องการเพิ่ม Widget ลงใน Dashboard ระบบจะ clone Object ต้นแบบนั้นมา 1 ชุด จากนั้นจึงนำการปรับแต่งของผู้ใช้ (ขนาด สี แหล่งข้อมูล) ไปใส่ใน Object ที่ clone มาได้

และนี่คือ code เมื่อประยุกต์ใช้ร่วมกับ Prototype

// Widget Settings Interface
interface WidgetSettings {
[key: string]: any;
}
class Widget {
type: string;
settings: WidgetSettings;
constructor(type: string, settings: WidgetSettings) {
this.type = type;
this.settings = settings;
}
clone(): Widget {
// Deep clone the settings object to ensure each widget has its own settings instance
const clonedSettings = JSON.parse(JSON.stringify(this.settings));
return new Widget(this.type, clonedSettings);
}
customize(customSettings: WidgetSettings): void {
// Apply custom settings to the widget
Object.assign(this.settings, customSettings);
}
display(): void {
console.log(`Displaying ${this.type} widget with settings:`, this.settings);
}
}
// Prototypes
const chartWidgetPrototype = new Widget('Chart', {
color: 'blue',
size: 'medium',
});
const tableWidgetPrototype = new Widget('Table', { rows: 5, columns: 3 });
// Usage
// User wants a chart widget with custom color
const userChartWidget = chartWidgetPrototype.clone();
userChartWidget.customize({ color: 'green' });
userChartWidget.display(); // Displaying Chart widget with settings: { color: 'green', size: 'medium' }
// User adds a standard table widget
const userTableWidget = tableWidgetPrototype.clone();
userTableWidget.display(); // Displaying Table widget with settings: { rows: 5, columns: 3 }

code ด้านบนเป็นตัวอย่างของการใช้รูปแบบ Prototype ในการสร้างและปรับแต่ง Object ประเภท Widget ใน web application ใน code โดย

  • มี Prototype คือ class Widget ที่ใช้ในการคัดลอกและสร้าง Object ใหม่ โดยมี method clone() เพื่อคัดลอก Object ต้นแบบ และ method customize() เพื่อปรับแต่ง Object ตามความต้องการของผู้ใช้
  • การใช้งาน สร้าง instance ของ Widget ต้นแบบ (prototype) เช่น chartWidgetPrototype และ tableWidgetPrototype และใช้ method clone() เพื่อคัดลอกวัตถุต้นแบบเพื่อสร้างวัตถุใหม่ และสามารถใช้ method customize() เพื่อปรับแต่งวัตถุตามความต้องการของผู้ใช้
  • ผู้ใช้สามารถสร้างและปรับแต่ง 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


Related Post
  • มาลองทำ Upload file แต่ละเคสกัน
    มี Video มี Github
    มาลองทำ upload ไฟล์ โดยจะลองมาทำ upload แบบทั่วไป, ทำ progress upload, validate file และ ยกเลิกการ upload
  • ลอง Rust Basic (2)
    มี Video
    มาทำความรู้จักกับภาษา Rust กันต่อ พาเจาะลึก Concept ที่จะช่วยทำให้เราสามารถนำไปพัฒนา application ที่มีความยืดหยุ่นได้มากขึ้น
  • Redux และ React
    มี Video มี Github
    รู้จักกับ Redux state management ที่ช่วยทำให้ application จัดการ state ได้สะดวกสบายยิ่งขึ้นกัน
  • รู้จักกับ Web Vitals guideline การสร้าง UX ที่ดีออกมากัน
    มี Video
    รู้จักกับคำศัพท์พื้นฐานของ Web Vitals และ use case ต่างๆของ Web Vitals กัน

Share on social media