Factory Method Pattern – Giải pháp cho việc tạo object mà không phụ thuộc vào class cụ thể

Table of contents
1. Vấn đề
Hãy tưởng tượng bạn đang viết một ứng dụng quản lý vận chuyển. Ban đầu, ứng dụng chỉ cần xử lý vận chuyển đường bộ bằng Truck, nên bạn code trực tiếp như sau:
class Truck {
deliver() {
console.log("Giao hàng bằng xe tải");
}
}
class OrderService {
createOrder() {
const transport = new Truck();
transport.deliver();
}
}
class ReportService {
generateReport() {
const transport = new Truck();
transport.deliver();
}
}
Ứng dụng chạy tốt. Nhưng khách hàng yêu cầu thêm Ship. Bạn sửa code như sau:
class OrderService {
createOrder(config: string) {
if (config === "road") {
const transport = new Truck();
transport.deliver();
} else if (config === "sea") {
const transport = new Ship();
transport.deliver();
}
}
}
class ReportService {
generateReport(config: string) {
if (config === "road") {
const transport = new Truck();
transport.deliver();
} else if (config === "sea") {
const transport = new Ship();
transport.deliver();
}
}
}
Vấn đề: if/else
bị lặp lại ở nhiều nơi. Nếu ngày mai có thêm Plane
, bạn lại phải sửa từng service. Hệ thống càng lớn, càng dễ lỗi.
2. Giải pháp
Factory Method tách biệt việc tạo object khỏi logic nghiệp vụ. Thay vì tạo object trực tiếp bằng new
, ta định nghĩa một factory method trong lớp cha (Creator). Các lớp con (Concrete Creator) sẽ quyết định tạo ra loại object cụ thể nào (Concrete Product).
Các thành phần chính
Product: interface hoặc abstract class mô tả bản thiết kế hành vi chung.
Concrete Product: class cài đặt cụ thể của Product.
Creator: lớp khai báo factory method, đồng thời chứa logic nghiệp vụ.
Concrete Creator: lớp con override factory method để tạo ra Concrete Product tương ứng.
// Product
interface Transport {
deliver(): void;
}
// Concrete Products
class Truck implements Transport {
deliver() { console.log("Giao hàng bằng xe tải"); }
}
class Ship implements Transport {
deliver() { console.log("Giao hàng bằng tàu biển"); }
}
// Creator
abstract class Logistics {
abstract createTransport(): Transport;
planDelivery() {
const transport = this.createTransport();
transport.deliver();
}
}
// Concrete Creators
class RoadLogistics extends Logistics {
createTransport(): Transport { return new Truck(); }
}
class SeaLogistics extends Logistics {
createTransport(): Transport { return new Ship(); }
}
// Client code
const logistics: Logistics =
config === "road" ? new RoadLogistics() : new SeaLogistics();
class OrderService {
constructor(private logistics: Logistics) {}
createOrder() {
this.logistics.planDelivery();
}
}
class ReportService {
constructor(private logistics: Logistics) {}
generateReport() {
this.logistics.planDelivery();
}
}
Bây giờ:
if/else
chỉ dùng một lần duy nhất (chọn Creator).Các service (Order, Report, …) không còn chứa
if/else
, chỉ làm việc với abstractionLogistics
.Muốn thêm
PlaneLogistics
→ chỉ cần viết thêm class mới, không phải sửa service nào.
3. Khi nào nên dùng
Khi code của bạn phụ thuộc vào nhiều loại object nhưng không muốn gắn chặt với class cụ thể.
Khi bạn cần dễ dàng mở rộng để hỗ trợ loại object mới mà không sửa logic chính.
Khi muốn giảm if/else hoặc switch-case liên quan đến việc tạo object.
Khi viết framework hoặc thư viện cho phép người dùng tuỳ biến cách tạo object.
4. Ưu và nhược điểm
Ưu điểm | Nhược điểm |
Giảm sự phụ thuộc vào class cụ thể. | Code trở nên phức tạp hơn vì có thêm nhiều class con. |
Dễ mở rộng theo Open/Closed Principle (mở rộng mà không sửa code cũ). | Nếu chỉ có ít loại object thì có thể cảm thấy “dùng dao mổ trâu giết gà”. |
Giúp gom logic khởi tạo vào một chỗ duy nhất (dễ bảo trì). | Có thể dẫn đến hệ thống class rườm rà. |
Subscribe to my newsletter
Read articles from Cao Trung Đức directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Cao Trung Đức
Cao Trung Đức
Thợ code học làm kinh tế