Trong thế giới lập trình hướng đối tượng, việc tạo ra các đối tượng một cách linh hoạt và có tổ chức là chìa khóa để xây dựng những hệ thống dễ bảo trì và mở rộng. Factory Method Pattern là một trong những mẫu thiết kế (Design Pattern) thuộc nhóm Creational Pattern (mẫu khởi tạo) quan trọng nhất giúp bạn giải quyết bài toán này một cách thanh lịch.
Bài viết này TASDIGITAL sẽ đi sâu vào Factory Method Pattern là gì, từ khái niệm cơ bản, cấu trúc, cách triển khai, cho đến ưu nhược điểm và khi nào nên sử dụng nó.
(Mô tả: Sơ đồ UML minh họa cấu trúc của Factory Method Pattern với Creator và Product.
1. Vấn đề cần giải quyết Factory Method Pattern là gì? 🤔
Hãy tưởng tượng bạn đang xây dựng một ứng dụng logistics. Ban đầu, bạn chỉ cần vận chuyển bằng xe tải (Truck). Code của bạn có thể trông như thế này:

// Mã giả
public class LogisticsApp {
public Transport getTransport(String type) {
if (type.equals("TRUCK")) {
return new Truck(); // Khởi tạo trực tiếp đối tượng Truck
}
// ... các loại khác
return null;
}
}Mọi thứ đều ổn cho đến khi sếp yêu cầu: “Chúng ta cần mở rộng, thêm vận chuyển bằng tàu thủy (Ship)”. Giờ đây, bạn phải sửa đổi trực tiếp class LogisticsApp:
// Mã giả
public class LogisticsApp {
public Transport getTransport(String type) {
if (type.equals("TRUCK")) {
return new Truck();
} else if (type.equals("SHIP")) {
return new Ship(); // Thêm logic mới
}
return null;
}
}Vấn đề là gì?
- Vi phạm Nguyên tắc Đóng/Mở (Open/Closed Principle): Mỗi khi có một phương thức vận chuyển mới (máy bay, tàu hỏa…), bạn phải sửa đổi code của class
LogisticsApp. Điều này làm tăng nguy cơ gây ra lỗi cho các chức năng đã chạy ổn định. - Phụ thuộc cứng (Tight Coupling): Class
LogisticsAppbị phụ thuộc cứng vào các class cụ thể nhưTruckvàShip. Nó phải biết tất cả các loại đối tượng mà nó có thể tạo ra.
Factory Method Pattern ra đời để giải quyết chính xác những vấn đề này.
2. Factory Method Pattern là gì?
Factory Method Pattern định nghĩa một giao diện (interface) hoặc một lớp trừu tượng (abstract class) để tạo ra một đối tượng, nhưng để các lớp con (subclasses) quyết định lớp nào (class) sẽ được khởi tạo. Nói một cách đơn giản, nó ủy quyền việc khởi tạo đối tượng cho các lớp con.

Hãy nghĩ về nó như một “nhà máy” (Factory). Nhà máy định nghĩa một phương thức createTransport() (phương thức factory), nhưng không quyết định sản phẩm cụ thể là gì. Các “nhà máy con” (ví dụ: nhà máy vận tải đường bộ, nhà máy vận tải đường biển) sẽ cài đặt phương thức này để tạo ra sản phẩm tương ứng (xe tải, tàu thủy).
3. Cấu trúc của Factory Method Pattern 🏗️

Pattern này bao gồm 4 thành phần chính:
- Product (Sản phẩm): Định nghĩa interface hoặc abstract class cho các đối tượng mà factory method sẽ tạo ra.
- Ví dụ:
Transportinterface.
- Ví dụ:
- ConcreteProduct (Sản phẩm cụ thể): Là các lớp cài đặt (implement) interface
Product. Đây là những đối tượng thực tế sẽ được tạo ra.- Ví dụ:
Truck,Shipclasses.
- Ví dụ:
- Creator (Người tạo): Là một abstract class hoặc interface khai báo
factoryMethod(). Phương thức này trả về một đối tượng kiểuProduct. Creator cũng có thể chứa các mã nghiệp vụ khác liên quan đếnProduct.- Ví dụ:
Logisticsabstract class.
- Ví dụ:
- ConcreteCreator (Người tạo cụ thể): Là các lớp con kế thừa từ
Creator. Chúng sẽ ghi đè (override)factoryMethod()để trả về mộtConcreteProductcụ thể.- Ví dụ:
RoadLogisticsclass (tạo ra Truck),SeaLogisticsclass (tạo ra Ship).
- Ví dụ:
(Ghi chú: Sơ đồ UML chi tiết 4 thành phần của Factory Method Pattern: Product, ConcreteProduct, Creator, ConcreteCreator.)
4. Ví dụ Factory Method Pattern triển khai bằng Java ☕

Hãy áp dụng pattern này để giải quyết bài toán logistics ở trên.
Bước 1: Tạo Product Interface và Concrete Products
// Product interface
interface Transport {
void deliver();
}
// ConcreteProduct 1
class Truck implements Transport {
@Override
public void deliver() {
System.out.println("Giao hàng bằng xe tải.");
}
}
// ConcreteProduct 2
class Ship implements Transport {
@Override
public void deliver() {
System.out.println("Giao hàng bằng tàu thủy.");
}
}Bước 2: Tạo Creator và Concrete Creators
// Creator (abstract class)
abstract class Logistics {
// Đây chính là Factory Method!
public abstract Transport createTransport();
// Các logic nghiệp vụ khác không cần thay đổi
public void planDelivery() {
Transport t = createTransport();
System.out.println("Chuẩn bị hàng hóa...");
t.deliver();
}
}
// ConcreteCreator 1
class RoadLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Truck(); // Lớp con quyết định đối tượng được tạo
}
}
// ConcreteCreator 2
class SeaLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Ship(); // Lớp con quyết định đối tượng được tạo
}
}Bước 3: Sử dụng trong Client Code
public class Application {
private static Logistics logistics;
public static void main(String[] args) {
// Tùy vào cấu hình hoặc lựa chọn, chúng ta khởi tạo nhà máy phù hợp
String config = "SEA"; // Giả sử đọc từ file config
if (config.equalsIgnoreCase("SEA")) {
logistics = new SeaLogistics();
} else {
logistics = new RoadLogistics();
}
// Mã client giờ đây chỉ làm việc với interface của Logistics
// mà không cần biết chi tiết bên trong
logistics.planDelivery();
}
}Kết quả: Nếu config là “SEA”, output sẽ là:
Chuẩn bị hàng hóa... Giao hàng bằng tàu thủy.
Nếu config là bất kỳ thứ gì khác, output sẽ là:
Chuẩn bị hàng hóa... Giao hàng bằng xe tải.
Giờ đây, nếu bạn muốn thêm vận chuyển bằng máy bay (AirLogistics tạo ra Airplane), bạn chỉ cần tạo các class mới mà không cần sửa đổi bất kỳ dòng code nào trong các class đã có. Hệ thống của bạn đã tuân thủ Nguyên tắc Đóng/Mở.
Xem thêm: Vanilla JavaScript Là Gì? Giải Mã Sức Mạnh Thuần Khiết Của JavaScript
5. Ưu và Nhược điểm
Ưu điểm 👍
- Giảm sự phụ thuộc cứng (Loose Coupling): Code của bạn không còn phụ thuộc vào các lớp
ConcreteProductmà chỉ làm việc vớiProductinterface. - Tuân thủ Nguyên tắc Đóng/Mở (Open/Closed Principle): Dễ dàng thêm các loại sản phẩm mới mà không cần sửa đổi mã nguồn hiện có của Creator.
- Tuân thủ Nguyên tắc Trách nhiệm đơn lẻ (Single Responsibility Principle): Tách biệt code sản xuất sản phẩm ra khỏi code sử dụng sản phẩm.
- Tăng tính linh hoạt: Việc tạo đối tượng được tập trung tại một nơi (các
ConcreteCreator), giúp dễ quản lý và thay đổi.
Nhược điểm 👎
- Tăng độ phức tạp: Bạn sẽ phải tạo thêm khá nhiều lớp và interface mới, điều này có thể làm phức tạp hóa code nếu bạn chỉ có một vài loại đối tượng đơn giản.
6. Khi nào nên sử dụng Factory Method Pattern?

Hãy sử dụng Factory Method Pattern khi:
- Bạn không biết trước chính xác loại đối tượng và sự phụ thuộc của chúng mà code của bạn sẽ làm việc.
- Bạn muốn cung cấp cho người dùng thư viện/framework của mình một cách để mở rộng các thành phần bên trong nó.
- Bạn muốn tái sử dụng code khởi tạo đối tượng hiện có để tránh việc trùng lặp code.
7. So sánh với các mẫu Creational khác
- Factory Method vs. Simple Factory: Simple Factory không phải là một mẫu design pattern chính thức trong GoF. Nó thường là một lớp duy nhất với một phương thức tĩnh để tạo đối tượng, vi phạm nguyên tắc Đóng/Mở. Factory Method thì linh hoạt hơn nhiều.
- Factory Method vs. Abstract Factory: Abstract Factory dùng để tạo ra một họ các đối tượng có liên quan (ví dụ: tạo ra
Button,Checkboxcho theme Windows hoặc macOS). Factory Method chỉ tạo ra một sản phẩm duy nhất. Bạn có thể dùng Factory Method để triển khai các phương thức trong một Abstract Factory. - Factory Method vs. Builder: Factory Method tập trung vào việc tạo đối tượng trong một bước. Builder tập trung vào việc xây dựng một đối tượng phức tạp qua nhiều bước.
Kết luận
Factory Method Pattern là một công cụ cực kỳ mạnh mẽ trong kho vũ khí của lập trình viên. Bằng cách ủy thác việc tạo đối tượng cho các lớp con, nó giúp xây dựng các hệ thống linh hoạt, dễ bảo trì và mở rộng, tuân thủ các nguyên tắc thiết kế SOLID quan trọng. Mặc dù có thể làm tăng số lượng lớp, nhưng lợi ích về cấu trúc và khả năng thích ứng mà nó mang lại trong các dự án lớn là không thể phủ nhận.

Công nghệ tương lai Lập trình/ Code
Portainer Là Gì? Toàn Tập Về Công Cụ Quản Trị Container Hàng Đầu (Hướng Dẫn Chi Tiết)
Sự bùng nổ của công nghệ Container hóa (Containerization) với đầu tàu là Docker
Xem thêmTh3
Công nghệ tương lai Công cụ và hướng dẫn Lập trình/ Code
Helper Là Gì? Bí Quyết Viết Code “Sạch” Và Tối Ưu Trong Lập Trình
Trong thế giới lập trình và phát triển phần mềm, việc phải lặp đi
Xem thêmTh3
Digital Maketing Đồ Họa và Video Xu hướng
Des là gì? Giải mã ý nghĩa của Des trong Thiết kế, SEO, IT & Logistics
Bạn đang lướt mạng xã hội và thấy ai đó bình luận: “Dân Des
Xem thêmTh3