1. Factory Pattern là gì?
Factory Pattern là một mẫu thiết kế (design pattern) thuộc nhóm Creational Patterns (mẫu thiết kế khởi tạo). Nó được sử dụng để tạo đối tượng mà không cần tiết lộ logic khởi tạo phức tạp với người dùng. Thay vào đó, chúng ta sử dụng một factory (nhà máy) để tạo ra các đối tượng này.
Hãy tưởng tượng một nhà máy sản xuất ô tô. Người mua chỉ cần đặt hàng (ví dụ: xe Sedan, SUV...) mà không cần biết chi tiết quy trình chế tạo. Nhà máy sẽ sản xuất chiếc xe theo yêu cầu và giao hàng.
2. Tại sao cần dùng Factory Pattern?
- Giảm sự phụ thuộc: Giúp tách rời phần logic khởi tạo đối tượng khỏi phần sử dụng chúng.
- Dễ mở rộng: Nếu cần thêm đối tượng mới, chỉ cần thay đổi trong factory mà không phải sửa mã ở nơi khác.
- Tăng tính linh hoạt: Người dùng không cần biết lớp cụ thể nào được sử dụng; factory đảm nhận việc này.
3. Cách hoạt động của Factory Pattern
Nó bao gồm các thành phần chính:
- Interface hoặc abstract class: Định nghĩa một kiểu chung cho các sản phẩm (objects).
- Concrete Classes: Các lớp cụ thể triển khai interface hoặc kế thừa abstract class.
- Factory Class: Lớp chịu trách nhiệm khởi tạo đối tượng và trả về kiểu chung (interface/abstract class).
4. Ví dụ dễ hiểu với thực tế: Quản lý phương tiện giao thông
Bài toán:
Bạn muốn xây dựng một ứng dụng quản lý phương tiện giao thông. Các loại phương tiện gồm Car, Bike, Truck.
Người dùng chỉ cần yêu cầu phương tiện mà không cần biết cách tạo chúng.
Code minh họa
Step 1: Tạo interface hoặc abstract class chung
// Đây là interface chung cho tất cả các phương tiện
public interface Vehicle { void drive();
}
Step 2: Tạo các lớp cụ thể
// Lớp Car
public class Car implements Vehicle { @Override public void drive() { System.out.println("Driving a Car!"); }
} // Lớp Bike
public class Bike implements Vehicle { @Override public void drive() { System.out.println("Riding a Bike!"); }
} // Lớp Truck
public class Truck implements Vehicle { @Override public void drive() { System.out.println("Driving a Truck!"); }
}
Step 3: Tạo Factory Class
public class VehicleFactory { // Phương thức tạo đối tượng dựa trên loại phương tiện yêu cầu public static Vehicle getVehicle(String vehicleType) { if (vehicleType == null) { return null; } switch (vehicleType.toLowerCase()) { case "car": return new Car(); case "bike": return new Bike(); case "truck": return new Truck(); default: throw new IllegalArgumentException("Unknown vehicle type: " + vehicleType); } }
}
Step 4: Sử dụng Factory trong ứng dụng
public class Main { public static void main(String[] args) { // Yêu cầu một Car từ Factory Vehicle car = VehicleFactory.getVehicle("car"); car.drive(); // Output: Driving a Car! // Yêu cầu một Bike từ Factory Vehicle bike = VehicleFactory.getVehicle("bike"); bike.drive(); // Output: Riding a Bike! // Yêu cầu một Truck từ Factory Vehicle truck = VehicleFactory.getVehicle("truck"); truck.drive(); // Output: Driving a Truck! }
}
5. Ưu và nhược điểm của Factory Pattern
Ưu điểm:
- Ẩn logic khởi tạo: Người dùng không cần biết chi tiết về việc tạo đối tượng.
- Tăng tính linh hoạt: Dễ dàng thay đổi hoặc mở rộng loại đối tượng mà không ảnh hưởng đến mã đã viết.
- Giảm sự phụ thuộc: Phần sử dụng chỉ phụ thuộc vào interface, không phụ thuộc vào lớp cụ thể.
Nhược điểm:
- Phức tạp hơn: So với việc khởi tạo trực tiếp đối tượng, cần thêm lớp factory.
- Khó bảo trì nếu quá nhiều loại sản phẩm: Nếu số lượng loại đối tượng quá lớn, mã trong factory có thể phức tạp và khó quản lý.
6. Khi nào nên sử dụng Factory Pattern?
- Khi cần tạo nhiều loại đối tượng liên quan hoặc tương tự nhau.
- Khi không muốn tiết lộ logic khởi tạo cho người dùng.
- Khi muốn làm việc với interface hoặc abstract class thay vì các lớp cụ thể.
7. Ví dụ thực tế khác:
Ứng dụng gửi thông báo (Notification Application)
Bạn có thể có các loại thông báo như Email, SMS, Push Notification, và sử dụng Factory Pattern để trả về thông báo phù hợp:
public interface Notification { void send(String message);
} public class EmailNotification implements Notification { @Override public void send(String message) { System.out.println("Sending Email: " + message); }
} public class SMSNotification implements Notification { @Override public void send(String message) { System.out.println("Sending SMS: " + message); }
} public class PushNotification implements Notification { @Override public void send(String message) { System.out.println("Sending Push Notification: " + message); }
} public class NotificationFactory { public static Notification getNotification(String type) { switch (type.toLowerCase()) { case "email": return new EmailNotification(); case "sms": return new SMSNotification(); case "push": return new PushNotification(); default: throw new IllegalArgumentException("Unknown notification type: " + type); } }
} public class Main { public static void main(String[] args) { Notification email = NotificationFactory.getNotification("email"); email.send("Welcome to Factory Pattern!"); Notification sms = NotificationFactory.getNotification("sms"); sms.send("Your OTP is 123456"); Notification push = NotificationFactory.getNotification("push"); push.send("You have a new message!"); }
}
Cảm ơn và bài viết còn nữa...
Cảm ơn mọi người đã đọc bài, mình sẽ cố gắng cập nhật ví dụ demo thực tế trong bài sớm nhất có thể.