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). Mẫu này cho phép tạo đối tượng mà không cần tiết lộ logic khởi tạo phức tạp cho người dùng. Thay vào đó, người dùng chỉ cần tương tác với một factory (nhà máy) để nhận các đối tượng mong muốn.
Hãy hình dung một nhà máy sản xuất ô tô. Người mua chỉ cần đặt hàng (ví dụ: ô tô Sedan, SUV...) mà không cần biết các chi tiết trong quy trình sản xuất. Nhà máy sẽ đảm bảo sản xuất chiếc xe theo yêu cầu và giao hàng đến tay người sử dụng.
2. Tại Sao Cần Sử Dụng Factory Pattern?
Factory Pattern mang lại nhiều lợi ích đáng kể:
- Giảm Sự Phụ Thuộc: Tách biệt phần logic khởi tạo đối tượng khỏi phần sử dụng, giúp mã nguồn dễ dàng bảo trì hơn.
- Dễ Dàng Mở Rộng: Việc thêm mới các loại đối tượng trở nên đơn giản, chỉ cần cập nhật trong factory mà không cần thay đổi mã tại 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 sẽ xử lý việc này, gia tăng tính mềm dẻo cho ứng dụng.
3. Cách Hoạt Động Của Factory Pattern
Mẫu thiết kế này bao gồm ba thành phần chính:
- Interface Hoặc Abstract Class: Định nghĩa 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 hoặc abstract class).
4. Ví Dụ Thực Tế Với Ứng Dụng Quản Lý Phương Tiện Giao Thông
Bài Toán:
Bạn phát triển một ứng dụng quản lý phương tiện giao thông, trong đó bao gồm các loại phương tiện như Car (Ô tô), Bike (Xe đạp), Truck (Xe tải). Người dùng sẽ yêu cầu phương tiện mà không cần biết cách tạo ra chúng.
Code Minh Họa
Bước 1: Tạo Interface Hoặc Abstract Class Chung
java
// Đây là interface chung cho tất cả các phương tiện
public interface Vehicle {
void drive();
}
Bước 2: Tạo Các Lớp Cụ Thể
java
// 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!");
}
}
Bước 3: Tạo Factory Class
java
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);
}
}
}
Bước 4: Sử Dụng Factory Trong Ứng Dụng
java
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, có thể gây khó khăn trong việc hiểu và bảo trì.
- Khó Bảo Trì Nếu Quá Nhiều Loại Sản Phẩm: Khi số lượng loại đối tượng tăng cao, mã trong factory có thể trở nên 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
Bạn có thể tạo ra các loại thông báo khác nhau như Email, SMS, Push Notification, sử dụng Factory Pattern để trả về thông báo phù hợp:
java
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ảm ơn các bạn đã theo dõi bài viết này. Mình sẽ cố gắng cập nhật thêm các ví dụ thực tế về Factory Pattern trong Java trong thời gian sớm nhất có thể.
source: viblo