Factory và Abstract Factory Pattern Giải Thích

Giới Thiệu
Singleton pattern đảm bảo bạn có một instance. Nhưng còn việc tạo nhiều instance — và làm điều đó theo cách linh hoạt, tách biệt thì sao?
Họ Factory patterns là về uỷ quyền việc tạo object. Thay vì gọi new SpecificClass() trực tiếp, bạn gọi một factory quyết định class nào sẽ được khởi tạo. Điều này giữ cho code gọi độc lập với các concrete class mà nó sử dụng.
Có hai pattern riêng biệt trong họ này:
| Pattern | Tác dụng |
|---|---|
| Factory Method | Định nghĩa interface để tạo object, nhưng để subclass quyết định class nào sẽ được khởi tạo |
| Abstract Factory | Cung cấp interface để tạo một họ các object liên quan mà không cần chỉ định concrete class của chúng |
Bạn Sẽ Học Được
✅ Hiểu vấn đề mà cả hai pattern giải quyết
✅ Implement Factory Method bằng TypeScript, Python, và Java
✅ Implement Abstract Factory bằng TypeScript, Python, và Java
✅ Biết khi nào dùng Factory Method vs Abstract Factory
✅ Khám phá các use case thực tế: UI toolkit, hệ thống thông báo, parser
✅ Tránh các anti-pattern thường gặp xung quanh việc lạm dụng factory
Điều Kiện Tiên Quyết
- Đã hoàn thành Singleton Pattern Giải Thích
- Quen thuộc với interface và abstract class (Polymorphism và Interfaces)
- Hiểu SOLID Principles, đặc biệt là Open/Closed
Vấn Đề: Tại Sao Không Chỉ Dùng new?
Hãy xem xét một hệ thống thông báo:
// ❌ Tightly coupled — khó mở rộng
class NotificationService {
send(type: string, message: string) {
if (type === 'email') {
const email = new EmailNotification();
email.send(message);
} else if (type === 'sms') {
const sms = new SMSNotification();
sms.send(message);
} else if (type === 'push') {
const push = new PushNotification();
push.send(message);
}
// Mỗi loại mới đều cần sửa class này ❌
}
}Vấn đề với cách tiếp cận này:
- Vi phạm Open/Closed Principle — mỗi loại thông báo mới đều cần sửa
NotificationService - Tightly coupled —
NotificationServicebiết về mọi concrete class - Khó test — bạn không thể inject mock mà không hack vào phần nội tại
- Logic trùng lặp — nếu các service khác cũng tạo thông báo,
if/elsenày bị lặp lại ở khắp nơi
Factory patterns giải quyết điều này bằng cách tập trung và trừu tượng hoá logic tạo object.
Phần 1: Factory Method Pattern
Ý Định
Định nghĩa interface để tạo một object, nhưng để subclass (hoặc implementation) quyết định class nào sẽ được khởi tạo. Factory method uỷ thác việc khởi tạo cho subclass.
Triển Khai TypeScript
// Product interface
interface Notification {
send(message: string): void;
getChannel(): string;
}
// Concrete products
class EmailNotification implements Notification {
constructor(private readonly recipient: string) {}
send(message: string): void {
console.log(`📧 Email đến ${this.recipient}: ${message}`);
}
getChannel(): string {
return 'email';
}
}
class SMSNotification implements Notification {
constructor(private readonly phoneNumber: string) {}
send(message: string): void {
console.log(`📱 SMS đến ${this.phoneNumber}: ${message}`);
}
getChannel(): string {
return 'sms';
}
}
class PushNotification implements Notification {
constructor(private readonly deviceToken: string) {}
send(message: string): void {
console.log(`🔔 Push đến ${this.deviceToken}: ${message}`);
}
getChannel(): string {
return 'push';
}
}
// Creator (abstract)
abstract class NotificationCreator {
// Factory Method — subclass quyết định tạo gì
abstract createNotification(recipient: string): Notification;
// Template method sử dụng factory method
notify(recipient: string, message: string): void {
const notification = this.createNotification(recipient);
console.log(`Gửi qua ${notification.getChannel()}...`);
notification.send(message);
}
}
// Concrete creators
class EmailNotificationCreator extends NotificationCreator {
createNotification(recipient: string): Notification {
return new EmailNotification(recipient);
}
}
class SMSNotificationCreator extends NotificationCreator {
createNotification(recipient: string): Notification {
return new SMSNotification(recipient);
}
}
class PushNotificationCreator extends NotificationCreator {
createNotification(recipient: string): Notification {
return new PushNotification(recipient);
}
}
// Sử dụng
const emailCreator = new EmailNotificationCreator();
emailCreator.notify('[email protected]', 'Đơn hàng của bạn đã được giao!');
// Gửi qua email...
// 📧 Email đến [email protected]: Đơn hàng của bạn đã được giao!
const smsCreator = new SMSNotificationCreator();
smsCreator.notify('+1234567890', 'Đơn hàng của bạn đã được giao!');
// Gửi qua sms...
// 📱 SMS đến +1234567890: Đơn hàng của bạn đã được giao!Simple Factory (Không Phải GoF Pattern, Nhưng Phổ Biến)
Trước khi đề cập Abstract Factory, lưu ý rằng nhiều codebase sử dụng Simple Factory — một static method hoặc class tập trung việc tạo mà không cần hierarchy kế thừa. Đơn giản hơn nhưng kém linh hoạt hơn:
// Simple Factory — không phải GoF pattern, nhưng thực tế
class NotificationFactory {
static create(
type: 'email' | 'sms' | 'push',
recipient: string
): Notification {
switch (type) {
case 'email':
return new EmailNotification(recipient);
case 'sms':
return new SMSNotification(recipient);
case 'push':
return new PushNotification(recipient);
default:
throw new Error(`Loại thông báo không xác định: ${type}`);
}
}
}
// Sử dụng — gọn gàng, tập trung, nhưng vẫn tightly coupled với các concrete type
const notification = NotificationFactory.create('email', '[email protected]');
notification.send('Xin chào!');Khi nào ưu tiên Simple Factory hơn Factory Method:
- Tập hợp nhỏ, ổn định các loại (không tăng thêm nhiều)
- Không cần mở rộng qua subclassing
- Prototype hoặc ứng dụng nhỏ hơn
Triển Khai Python
from abc import ABC, abstractmethod
from typing import Protocol
class Notification(Protocol):
def send(self, message: str) -> None: ...
def get_channel(self) -> str: ...
class EmailNotification:
def __init__(self, recipient: str) -> None:
self.recipient = recipient
def send(self, message: str) -> None:
print(f"📧 Email đến {self.recipient}: {message}")
def get_channel(self) -> str:
return "email"
class SMSNotification:
def __init__(self, phone_number: str) -> None:
self.phone_number = phone_number
def send(self, message: str) -> None:
print(f"📱 SMS đến {self.phone_number}: {message}")
def get_channel(self) -> str:
return "sms"
class NotificationCreator(ABC):
@abstractmethod
def create_notification(self, recipient: str) -> Notification:
pass
def notify(self, recipient: str, message: str) -> None:
notification = self.create_notification(recipient)
print(f"Gửi qua {notification.get_channel()}...")
notification.send(message)
class EmailNotificationCreator(NotificationCreator):
def create_notification(self, recipient: str) -> Notification:
return EmailNotification(recipient)
class SMSNotificationCreator(NotificationCreator):
def create_notification(self, recipient: str) -> Notification:
return SMSNotification(recipient)
# Sử dụng
creator = EmailNotificationCreator()
creator.notify("[email protected]", "Đơn hàng của bạn đã được giao!")
# Gửi qua email...
# 📧 Email đến [email protected]: Đơn hàng của bạn đã được giao!Triển Khai Java
// Product interface
public interface Notification {
void send(String message);
String getChannel();
}
// Concrete products
public class EmailNotification implements Notification {
private final String recipient;
public EmailNotification(String recipient) {
this.recipient = recipient;
}
@Override
public void send(String message) {
System.out.println("📧 Email đến " + recipient + ": " + message);
}
@Override
public String getChannel() { return "email"; }
}
public class SMSNotification implements Notification {
private final String phoneNumber;
public SMSNotification(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public void send(String message) {
System.out.println("📱 SMS đến " + phoneNumber + ": " + message);
}
@Override
public String getChannel() { return "sms"; }
}
// Creator (abstract class với factory method)
public abstract class NotificationCreator {
// Factory Method — subclass override cái này
public abstract Notification createNotification(String recipient);
// Template method
public void notify(String recipient, String message) {
Notification notification = createNotification(recipient);
System.out.println("Gửi qua " + notification.getChannel() + "...");
notification.send(message);
}
}
// Concrete creators
public class EmailNotificationCreator extends NotificationCreator {
@Override
public Notification createNotification(String recipient) {
return new EmailNotification(recipient);
}
}
public class SMSNotificationCreator extends NotificationCreator {
@Override
public Notification createNotification(String recipient) {
return new SMSNotification(recipient);
}
}
// Sử dụng
NotificationCreator creator = new EmailNotificationCreator();
creator.notify("[email protected]", "Đơn hàng của bạn đã được giao!");Phần 2: Abstract Factory Pattern
Ý Định
Cung cấp interface để tạo các họ object liên quan mà không cần chỉ định concrete class của chúng. Hãy nghĩ đây là "factory của các factory" — mỗi concrete factory tạo ra một tập hợp các product liên quan nhất quán, hoàn chỉnh.
Khi Nào Dùng Abstract Factory vs Factory Method
| Factory Method | Abstract Factory | |
|---|---|---|
| Tạo ra | Một loại product | Nhiều loại product liên quan |
| Cơ chế | Kế thừa (subclass override) | Composition (inject factory interface) |
| Dùng khi | Một điểm biến đổi | Nhiều điểm biến đổi nhất quán |
| Ví dụ | Các kênh thông báo khác nhau | UI đa nền tảng (button + checkbox + input) |
Ví Dụ Điển Hình: UI Đa Nền Tảng
Triển Khai TypeScript
// Abstract products
interface Button {
render(): void;
onClick(handler: () => void): void;
}
interface Checkbox {
render(): void;
toggle(): void;
isChecked(): boolean;
}
interface TextInput {
render(): void;
getValue(): string;
setValue(value: string): void;
}
// Concrete products — họ Windows
class WindowsButton implements Button {
private handler?: () => void;
render(): void {
console.log('[Windows] Rendering button với style Win32');
}
onClick(handler: () => void): void {
this.handler = handler;
}
}
class WindowsCheckbox implements Checkbox {
private checked = false;
render(): void {
console.log(`[Windows] Rendering checkbox (${this.checked ? '✓' : '○'})`);
}
toggle(): void {
this.checked = !this.checked;
}
isChecked(): boolean {
return this.checked;
}
}
class WindowsTextInput implements TextInput {
private value = '';
render(): void {
console.log(`[Windows] Rendering text input: "${this.value}"`);
}
getValue(): string {
return this.value;
}
setValue(value: string): void {
this.value = value;
}
}
// Concrete products — họ Mac
class MacButton implements Button {
private handler?: () => void;
render(): void {
console.log('[Mac] Rendering button với style macOS');
}
onClick(handler: () => void): void {
this.handler = handler;
}
}
class MacCheckbox implements Checkbox {
private checked = false;
render(): void {
console.log(`[Mac] Rendering checkbox (${this.checked ? '●' : '○'})`);
}
toggle(): void {
this.checked = !this.checked;
}
isChecked(): boolean {
return this.checked;
}
}
class MacTextInput implements TextInput {
private value = '';
render(): void {
console.log(`[Mac] Rendering text input: "${this.value}"`);
}
getValue(): string {
return this.value;
}
setValue(value: string): void {
this.value = value;
}
}
// Abstract Factory interface
interface UIFactory {
createButton(): Button;
createCheckbox(): Checkbox;
createTextInput(): TextInput;
}
// Concrete factories — mỗi cái tạo ra một HỌ nhất quán
class WindowsUIFactory implements UIFactory {
createButton(): Button {
return new WindowsButton();
}
createCheckbox(): Checkbox {
return new WindowsCheckbox();
}
createTextInput(): TextInput {
return new WindowsTextInput();
}
}
class MacUIFactory implements UIFactory {
createButton(): Button {
return new MacButton();
}
createCheckbox(): Checkbox {
return new MacCheckbox();
}
createTextInput(): TextInput {
return new MacTextInput();
}
}
// Application — chỉ phụ thuộc vào abstract factory
class LoginForm {
private button: Button;
private usernameInput: TextInput;
private rememberMe: Checkbox;
constructor(factory: UIFactory) {
this.button = factory.createButton();
this.usernameInput = factory.createTextInput();
this.rememberMe = factory.createCheckbox();
}
render(): void {
this.usernameInput.render();
this.rememberMe.render();
this.button.render();
}
}
// Sử dụng — inject factory phù hợp khi khởi động
function createFactory(): UIFactory {
const os = process.platform;
if (os === 'win32') return new WindowsUIFactory();
if (os === 'darwin') return new MacUIFactory();
return new MacUIFactory(); // mặc định
}
const factory = createFactory();
const loginForm = new LoginForm(factory);
loginForm.render();
// [Mac] Rendering text input: ""
// [Mac] Rendering checkbox (○)
// [Mac] Rendering button với style macOSTriển Khai Python
from abc import ABC, abstractmethod
import sys
class Button(ABC):
@abstractmethod
def render(self) -> None: ...
@abstractmethod
def on_click(self, handler) -> None: ...
class Checkbox(ABC):
@abstractmethod
def render(self) -> None: ...
@abstractmethod
def toggle(self) -> None: ...
@abstractmethod
def is_checked(self) -> bool: ...
class TextInput(ABC):
@abstractmethod
def render(self) -> None: ...
@abstractmethod
def get_value(self) -> str: ...
@abstractmethod
def set_value(self, value: str) -> None: ...
# Họ Windows
class WindowsButton(Button):
def render(self) -> None:
print("[Windows] Rendering button với style Win32")
def on_click(self, handler) -> None:
self._handler = handler
class WindowsCheckbox(Checkbox):
def __init__(self):
self._checked = False
def render(self) -> None:
symbol = "✓" if self._checked else "○"
print(f"[Windows] Rendering checkbox ({symbol})")
def toggle(self) -> None:
self._checked = not self._checked
def is_checked(self) -> bool:
return self._checked
class WindowsTextInput(TextInput):
def __init__(self):
self._value = ""
def render(self) -> None:
print(f'[Windows] Rendering text input: "{self._value}"')
def get_value(self) -> str:
return self._value
def set_value(self, value: str) -> None:
self._value = value
# Họ Mac
class MacButton(Button):
def render(self) -> None:
print("[Mac] Rendering button với style macOS")
def on_click(self, handler) -> None:
self._handler = handler
class MacCheckbox(Checkbox):
def __init__(self):
self._checked = False
def render(self) -> None:
symbol = "●" if self._checked else "○"
print(f"[Mac] Rendering checkbox ({symbol})")
def toggle(self) -> None:
self._checked = not self._checked
def is_checked(self) -> bool:
return self._checked
class MacTextInput(TextInput):
def __init__(self):
self._value = ""
def render(self) -> None:
print(f'[Mac] Rendering text input: "{self._value}"')
def get_value(self) -> str:
return self._value
def set_value(self, value: str) -> None:
self._value = value
# Abstract Factory
class UIFactory(ABC):
@abstractmethod
def create_button(self) -> Button: ...
@abstractmethod
def create_checkbox(self) -> Checkbox: ...
@abstractmethod
def create_text_input(self) -> TextInput: ...
class WindowsUIFactory(UIFactory):
def create_button(self) -> Button:
return WindowsButton()
def create_checkbox(self) -> Checkbox:
return WindowsCheckbox()
def create_text_input(self) -> TextInput:
return WindowsTextInput()
class MacUIFactory(UIFactory):
def create_button(self) -> Button:
return MacButton()
def create_checkbox(self) -> Checkbox:
return MacCheckbox()
def create_text_input(self) -> TextInput:
return MacTextInput()
class LoginForm:
def __init__(self, factory: UIFactory) -> None:
self.button = factory.create_button()
self.username_input = factory.create_text_input()
self.remember_me = factory.create_checkbox()
def render(self) -> None:
self.username_input.render()
self.remember_me.render()
self.button.render()
# Sử dụng
factory: UIFactory
if sys.platform == "win32":
factory = WindowsUIFactory()
else:
factory = MacUIFactory()
login_form = LoginForm(factory)
login_form.render()Triển Khai Java
// Abstract products
public interface Button {
void render();
void onClick(Runnable handler);
}
public interface Checkbox {
void render();
void toggle();
boolean isChecked();
}
public interface TextInput {
void render();
String getValue();
void setValue(String value);
}
// Họ Windows
public class WindowsButton implements Button {
@Override
public void render() {
System.out.println("[Windows] Rendering button với style Win32");
}
@Override
public void onClick(Runnable handler) { /* đăng ký handler */ }
}
public class WindowsCheckbox implements Checkbox {
private boolean checked = false;
@Override
public void render() {
System.out.println("[Windows] Rendering checkbox (" + (checked ? "✓" : "○") + ")");
}
@Override
public void toggle() { checked = !checked; }
@Override
public boolean isChecked() { return checked; }
}
// Abstract Factory interface
public interface UIFactory {
Button createButton();
Checkbox createCheckbox();
TextInput createTextInput();
}
// Concrete factories
public class WindowsUIFactory implements UIFactory {
@Override
public Button createButton() { return new WindowsButton(); }
@Override
public Checkbox createCheckbox() { return new WindowsCheckbox(); }
@Override
public TextInput createTextInput() { return new WindowsTextInput(); }
}
public class MacUIFactory implements UIFactory {
@Override
public Button createButton() { return new MacButton(); }
@Override
public Checkbox createCheckbox() { return new MacCheckbox(); }
@Override
public TextInput createTextInput() { return new MacTextInput(); }
}
// Application — chỉ phụ thuộc vào interface
public class LoginForm {
private final Button button;
private final TextInput usernameInput;
private final Checkbox rememberMe;
public LoginForm(UIFactory factory) {
this.button = factory.createButton();
this.usernameInput = factory.createTextInput();
this.rememberMe = factory.createCheckbox();
}
public void render() {
usernameInput.render();
rememberMe.render();
button.render();
}
}
// Sử dụng
UIFactory factory = getFactoryForOS(); // trả về Windows hoặc Mac factory
LoginForm form = new LoginForm(factory);
form.render();Use Case Thực Tế: Tầng Truy Cập Database
Abstract Factory lý tưởng để trừu tượng hoá các implementation cụ thể cho từng database:
// Abstract products
interface DatabaseConnection {
query<T>(sql: string, params?: unknown[]): Promise<T[]>;
close(): Promise<void>;
}
interface DatabaseTransaction {
begin(): Promise<void>;
commit(): Promise<void>;
rollback(): Promise<void>;
query<T>(sql: string, params?: unknown[]): Promise<T[]>;
}
interface DatabaseConnectionPool {
acquire(): Promise<DatabaseConnection>;
release(conn: DatabaseConnection): void;
drain(): Promise<void>;
}
// Abstract factory
interface DatabaseFactory {
createConnection(config: DatabaseConfig): DatabaseConnection;
createTransaction(conn: DatabaseConnection): DatabaseTransaction;
createPool(config: PoolConfig): DatabaseConnectionPool;
}
// Concrete factories
class PostgreSQLFactory implements DatabaseFactory {
createConnection(config: DatabaseConfig): DatabaseConnection {
return new PostgreSQLConnection(config);
}
createTransaction(conn: DatabaseConnection): DatabaseTransaction {
return new PostgreSQLTransaction(conn as PostgreSQLConnection);
}
createPool(config: PoolConfig): DatabaseConnectionPool {
return new PostgreSQLPool(config);
}
}
class SQLiteFactory implements DatabaseFactory {
createConnection(config: DatabaseConfig): DatabaseConnection {
return new SQLiteConnection(config);
}
createTransaction(conn: DatabaseConnection): DatabaseTransaction {
return new SQLiteTransaction(conn as SQLiteConnection);
}
createPool(config: PoolConfig): DatabaseConnectionPool {
return new SQLitePool(config); // SQLite xử lý pooling khác đi
}
}
// Repository chỉ phụ thuộc vào các abstract interface
class UserRepository {
constructor(
private readonly factory: DatabaseFactory,
private readonly config: DatabaseConfig
) {}
async findById(id: number): Promise<User | null> {
const conn = this.factory.createConnection(this.config);
try {
const rows = await conn.query<User>(
'SELECT * FROM users WHERE id = $1', [id]
);
return rows[0] ?? null;
} finally {
await conn.close();
}
}
async createWithTransaction(userData: CreateUserInput): Promise<User> {
const conn = this.factory.createConnection(this.config);
const tx = this.factory.createTransaction(conn);
await tx.begin();
try {
const [user] = await tx.query<User>(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
[userData.name, userData.email]
);
await tx.commit();
return user;
} catch (error) {
await tx.rollback();
throw error;
} finally {
await conn.close();
}
}
}
// Sử dụng — đổi database bằng cách hoán đổi factory
const factory = process.env.NODE_ENV === 'test'
? new SQLiteFactory() // In-memory nhanh cho test
: new PostgreSQLFactory(); // Database production
const userRepo = new UserRepository(factory, dbConfig);Use Case Thực Tế: Hệ Thống Parser
Factory Method là lựa chọn tự nhiên cho các họ parser:
interface Document {
getContent(): string;
getMetadata(): Record<string, string>;
}
interface DocumentParser {
parse(input: string): Document;
}
// Factory Method pattern — mỗi subclass xử lý một định dạng
abstract class DocumentProcessor {
// Factory method
protected abstract createParser(): DocumentParser;
// Template method sử dụng factory method
process(input: string): ProcessedDocument {
const parser = this.createParser();
const doc = parser.parse(input);
return this.transform(doc);
}
private transform(doc: Document): ProcessedDocument {
return {
content: doc.getContent().trim(),
metadata: doc.getMetadata(),
processedAt: new Date(),
};
}
}
class JSONDocumentProcessor extends DocumentProcessor {
protected createParser(): DocumentParser {
return new JSONParser();
}
}
class XMLDocumentProcessor extends DocumentProcessor {
protected createParser(): DocumentParser {
return new XMLParser();
}
}
class MarkdownDocumentProcessor extends DocumentProcessor {
protected createParser(): DocumentParser {
return new MarkdownParser();
}
}
// Registry pattern — chọn processor phù hợp tại runtime
const processorRegistry: Record<string, DocumentProcessor> = {
json: new JSONDocumentProcessor(),
xml: new XMLDocumentProcessor(),
md: new MarkdownDocumentProcessor(),
};
function processDocument(format: string, input: string): ProcessedDocument {
const processor = processorRegistry[format];
if (!processor) throw new Error(`Định dạng không được hỗ trợ: ${format}`);
return processor.process(input);
}Test Với Factories
Một lợi ích lớn của factory: dễ dàng test với mock.
// Test double factory — trả về object có thể dự đoán
class MockUIFactory implements UIFactory {
createButton(): Button {
return {
render: jest.fn(),
onClick: jest.fn(),
};
}
createCheckbox(): Checkbox {
return {
render: jest.fn(),
toggle: jest.fn(),
isChecked: jest.fn().mockReturnValue(false),
};
}
createTextInput(): TextInput {
return {
render: jest.fn(),
getValue: jest.fn().mockReturnValue(''),
setValue: jest.fn(),
};
}
}
// Test
describe('LoginForm', () => {
it('render tất cả component khi khởi tạo', () => {
const mockFactory = new MockUIFactory();
const form = new LoginForm(mockFactory);
form.render();
// Xác minh mỗi component được yêu cầu render
const button = mockFactory.createButton();
expect(button.render).toHaveBeenCalled();
});
});
// Tương tự cho ví dụ database:
class MockDatabaseFactory implements DatabaseFactory {
createConnection(): DatabaseConnection {
return {
query: jest.fn().mockResolvedValue([]),
close: jest.fn().mockResolvedValue(undefined),
};
}
createTransaction(conn: DatabaseConnection): DatabaseTransaction {
return {
begin: jest.fn().mockResolvedValue(undefined),
commit: jest.fn().mockResolvedValue(undefined),
rollback: jest.fn().mockResolvedValue(undefined),
query: jest.fn().mockResolvedValue([]),
};
}
createPool(): DatabaseConnectionPool {
return {
acquire: jest.fn(),
release: jest.fn(),
drain: jest.fn().mockResolvedValue(undefined),
};
}
}
describe('UserRepository', () => {
it('trả về null khi không tìm thấy user', async () => {
const factory = new MockDatabaseFactory();
const repo = new UserRepository(factory, testConfig);
const user = await repo.findById(999);
expect(user).toBeNull();
});
});Khi Nào Dùng Mỗi Pattern
Tóm Tắt
| Tình huống | Pattern Nên Dùng |
|---|---|
| Tạo một loại product, subclass quyết định loại nào | Factory Method |
| Tạo một họ product nhất quán | Abstract Factory |
| Tập hợp nhỏ, cố định các loại, không cần subclassing | Simple Factory |
| Cần hoán đổi toàn bộ họ product (OS, DB, theme) | Abstract Factory |
| Framework yêu cầu bạn override một creation method | Factory Method |
Các Lỗi Thường Gặp
1. Over-engineering với Abstract Factory
// ❌ Quá phức tạp — Abstract Factory cho một loại product duy nhất
interface ButtonFactory {
createButton(): Button; // Chỉ một product? Dùng Factory Method thay thế
}
// ✅ Abstract Factory dành cho họ các product
interface UIFactory {
createButton(): Button;
createCheckbox(): Checkbox; // Nhiều product liên quan
createTextInput(): TextInput; // cần giữ nhất quán
}2. Factory biết quá nhiều thứ
// ❌ Factory có business logic — vi phạm Single Responsibility
class UserFactory {
static create(data: UserInput): User {
const user = new User(data);
user.sendWelcomeEmail(); // ❌ Không phải việc của factory
database.save(user); // ❌ Không phải việc của factory
auditLog.record(user); // ❌ Không phải việc của factory
return user;
}
}
// ✅ Factory chỉ tạo object
class UserFactory {
static create(data: UserInput): User {
return new User(data);
}
}3. Quên đăng ký loại mới
// ❌ Registry-based factory không báo lỗi rõ ràng
class ParserFactory {
private static parsers: Map<string, DocumentParser> = new Map([
['json', new JSONParser()],
['xml', new XMLParser()],
// Developer mới thêm MarkdownParser nhưng quên đăng ký ở đây
]);
static create(format: string): DocumentParser {
return this.parsers.get(format) ?? new JSONParser(); // ❌ Fallback thầm lặng
}
}
// ✅ Báo lỗi rõ ràng
static create(format: string): DocumentParser {
const parser = this.parsers.get(format);
if (!parser) {
throw new Error(
`Không có parser nào được đăng ký cho định dạng: "${format}". ` +
`Các định dạng được đăng ký: ${[...this.parsers.keys()].join(', ')}`
);
}
return parser;
}Tóm Tắt và Điểm Mấu Chốt
Factory Method:
✅ Tập trung việc tạo một loại product
✅ Để subclass quyết định concrete class nào được khởi tạo
✅ Tuyệt vời cho framework mà user override các creation hook
✅ Tuân theo Open/Closed Principle — thêm product mới mà không sửa creator hiện có
Abstract Factory:
✅ Tạo họ các product liên quan phải nhất quán
✅ Client phụ thuộc chỉ vào abstract interface — không bao giờ phụ thuộc vào concrete class
✅ Hoàn hảo cho hệ thống đa nền tảng, đa database, hoặc đa theme
✅ Làm cho việc hoán đổi toàn bộ họ trở nên dễ dàng (ví dụ: Windows → Mac UI)
Bước Tiếp Theo
- OOP-10: Builder Pattern — khi object có nhiều phần tùy chọn và các bước lắp ráp phức tạp
- OOP-12: Adapter và Facade Pattern — structural pattern để làm việc với interface không tương thích
- OOP-13: Decorator và Proxy Pattern — thêm hành vi động mà không cần subclassing
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.