- vừa được xem lúc

Factory Method Pattern trong Java

0 0 1

Người đăng: Ông Huy Thắng

Theo Viblo Asia

Factory Method Pattern trong Java là gì?

Factory Pattern là một Creational Design Pattern (mẫu thiết kế tạo đối tượng), giúp tạo đối tượng mà không cần chỉ rõ lớp cụ thể nào sẽ được khởi tạo. Thay vì dùng từ khóa new trực tiếp, bạn gọi một factory method, và method đó sẽ trả về một instance phù hợp.

// Giao diện
public interface Notification { void notifyUser();
} // Các class cụ thể
public class EmailNotification implements Notification { public void notifyUser() { System.out.println("Sending Email Notification"); }
} public class SMSNotification implements Notification { public void notifyUser() { System.out.println("Sending SMS Notification"); }
} // Factory
public class NotificationFactory { public static Notification createNotification(String type) { if ("EMAIL".equalsIgnoreCase(type)) { return new EmailNotification(); } else if ("SMS".equalsIgnoreCase(type)) { return new SMSNotification(); } throw new IllegalArgumentException("Unknown type"); }
} // Sử dụng
Notification notification = NotificationFactory.createNotification("EMAIL");
notification.notifyUser();

Áp dụng thực tế trong Spring Boot

Trong Spring Boot, bạn thường không cần viết Factory thủ công như trên vì Spring đã có Dependency Injection (DI) để quản lý bean. Tuy nhiên, Factory Pattern vẫn hữu ích trong các trường hợp như:

Use case 1: Chọn strategy theo loại yêu cầu

Ví dụ: xử lý các loại thanh toán khác nhau.

public interface PaymentService { void pay();
} @Service("creditCard")
public class CreditCardPaymentService implements PaymentService { public void pay() { System.out.println("Paying by credit card"); }
} @Service("paypal")
public class PaypalPaymentService implements PaymentService { public void pay() { System.out.println("Paying by PayPal"); }
}
@Component
public class PaymentFactory { private final Map<String, PaymentService> serviceMap; @Autowired public PaymentFactory(List<PaymentService> services) { serviceMap = new HashMap<>(); for (PaymentService service : services) { String beanName = service.getClass().getAnnotation(Service.class).value(); serviceMap.put(beanName, service); } } public PaymentService getPaymentService(String type) { return serviceMap.get(type.toLowerCase()); }
}
@RestController
public class PaymentController { @Autowired private PaymentFactory factory; @PostMapping("/pay") public void pay(@RequestParam String method) { PaymentService service = factory.getPaymentService(method); service.pay(); }
}

Use case 2: Tùy biến logic theo cấu hình hoặc loại hình dịch vụ

Xử lý tạo đơn vay vốn khác nhau tùy theo loại khách hàng: cá nhân (PERSONAL) hoặc doanh nghiệp (CORPORATE).

Bài toán:

Ứng dụng có 2 loại khách hàng:

  • PERSONAL: xử lý logic A.
  • CORPORATE: xử lý logic B.

Bạn cần xây dựng một factory để trả về service tương ứng và xử lý logic tạo đơn vay theo đúng loại khách hàng.

// Tạo interface dùng chung
public interface LoanApplicationService { void createLoanApplication(LoanRequest request);
}
// Tạo 2 class implement logic riêng biệt
@Service("personal")
public class PersonalLoanService implements LoanApplicationService { @Override public void createLoanApplication(LoanRequest request) { System.out.println("Processing loan for PERSONAL customer: " + request); }
} @Service("corporate")
public class CorporateLoanService implements LoanApplicationService { @Override public void createLoanApplication(LoanRequest request) { System.out.println("Processing loan for CORPORATE customer: " + request); }
}
// Tạo lớp Factory để chọn service phù hợp
@Component
public class LoanServiceFactory { private final Map<String, LoanApplicationService> serviceMap = new HashMap<>(); @Autowired public LoanServiceFactory(List<LoanApplicationService> services) { for (LoanApplicationService service : services) { Service annotation = service.getClass().getAnnotation(Service.class); if (annotation != null) { serviceMap.put(annotation.value().toLowerCase(), service); } } } public LoanApplicationService getService(String customerType) { LoanApplicationService service = serviceMap.get(customerType.toLowerCase()); if (service == null) { throw new IllegalArgumentException("Unsupported customer type: " + customerType); } return service; }
}
// Controller sử dụng Factory
@RestController
@RequestMapping("/loan")
public class LoanController { private final LoanServiceFactory loanServiceFactory; public LoanController(LoanServiceFactory loanServiceFactory) { this.loanServiceFactory = loanServiceFactory; } @PostMapping("/apply") public ResponseEntity<String> applyLoan(@RequestBody LoanRequest request) { String customerType = request.getCustomerType(); // e.g., "personal" or "corporate" LoanApplicationService service = loanServiceFactory.getService(customerType); service.createLoanApplication(request); return ResponseEntity.ok("Loan application processed for: " + customerType); }
}
// DTO request
public class LoanRequest { private String customerType; private String customerName; private BigDecimal loanAmount; // Getters/setters/toString...
}

Kết quả:

  • Khi người dùng gửi POST /loan/apply với customerType = personal, hệ thống sẽ dùng PersonalLoanService.
  • Khi là corporate, nó sẽ dùng CorporateLoanService.
  • Bạn có thể dễ dàng mở rộng thêm loại VIP, FOREIGNER,... mà không cần sửa controller hay factory, chỉ cần tạo class mới và đánh @Service("vip").

Bạn có thể nâng cấp đoạn mã trên bằng Dynamic Bean

Dynamic Bean Resolution (Tra cứu Bean động) thông qua ApplicationContext (hoặc custom SpringContext wrapper).

Yêu cầu:

Thay vì inject map hoặc factory, bạn dùng tên Bean động để lấy ra LoanApplicationService phù hợp với loại khách hàng.

// Interface chuẩn
public interface LoanApplicationService { void createLoanApplication(LoanRequest request);
}
// Implement cụ thể và đặt tên Bean theo quy ước
@Component("loan.personal") // @Service
public class PersonalLoanService implements LoanApplicationService { @Override public void createLoanApplication(LoanRequest request) { System.out.println("Processing loan for PERSONAL customer: " + request); }
} @Component("loan.corporate")
public class CorporateLoanService implements LoanApplicationService { @Override public void createLoanApplication(LoanRequest request) { System.out.println("Processing loan for CORPORATE customer: " + request); }
}
// Tạo class tiện ích SpringContext (nếu chưa có)
@Component
public class SpringContext implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext ctx) { context = ctx; } public static <T> T getBean(String name, Class<T> clazz) { return context.getBean(name, clazz); }
}
// Controller dùng dynamic bean resolution
@RestController
@RequestMapping("/loan")
public class LoanController { @PostMapping("/apply") public ResponseEntity<String> applyLoan(@RequestBody LoanRequest request) { try { String customerType = request.getCustomerType().toLowerCase(); // e.g., "personal", "corporate" String beanName = "loan." + customerType; LoanApplicationService service = SpringContext.getBean(beanName, LoanApplicationService.class); service.createLoanApplication(request); return ResponseEntity.ok("Loan processed for: " + customerType); } catch (NoSuchBeanDefinitionException e) { return ResponseEntity .status(HttpStatus.BAD_REQUEST) .body("Unsupported customer type: " + request.getCustomerType()); } }
}

Cách trên sẽ có vài ưu điểm hơn:

  • Dynamic & flexible: Không cần inject toàn bộ bean, chỉ lấy đúng cái mình cần
  • Dễ mở rộng: Muốn thêm loan.vip chỉ cần tạo thêm 1 class mới
  • Fail-safe xử lý lỗi: Có thể bắt NoSuchBeanDefinitionException để báo "Loan type không hỗ trợ" Không cần factory class riêng biệt: Tối giản code, giữ controller rõ ràng

Tại sao không dùng if-else hay switch luôn?

Vì:

  • Dễ mở rộng: Chỉ cần thêm bean mới, không phải sửa if-else.
  • Tuân thủ Open/Closed Principle (mở để mở rộng, đóng để chỉnh sửa).
  • Code rõ ràng, tách biệt logic khởi tạo khỏi logic xử lý.

Tổng kết:

Yếu tố Factory Pattern Spring Boot
Tạo đối tượng Dựa vào logic tùy biến Dựa vào DI và @Autowired
Khi nào dùng Khi không biết class cụ thể Khi cần chọn bean theo loại runtime
Lợi ích Giảm phụ thuộc, dễ mở rộng Tận dụng DI để tự động wiring

Bình luận

Bài viết tương tự

- vừa được xem lúc

Một ví dụ nhỏ về Factory method

Trong bài viết trước mình đã giới thiệu tới các bạn về Abstract Factory pattern, các bạn quan tâm có thể theo dõi lại tại đây. Để tiếp tục về chủ đề design pattern trong bài viết này mình sẽ trình bày những khái niệm, ưu nhược điểm và các sử dụng của một creational design pattern khác đó là Factory

0 0 41

- vừa được xem lúc

Factory Method Design Pattern - Trợ thủ đắc lực của Developers

1. Giới thiệu.

0 0 51

- vừa được xem lúc

Signleton Desgin Pattern - Trợ thủ đắc lực của Developers

1. Giới thiệu.

0 0 42

- vừa được xem lúc

Prototype Design Pattern - Trợ thủ đắc lực của Developers

1. Giới thiệu. . Prototype là một design pattern thuộc nhóm Creational Pattern - những mẫu thiết kế cho việc khởi tạo object của lớp.

0 0 45

- vừa được xem lúc

Builder Design Pattern - Trợ thủ đắc lực của Developers

1. Giới thiệu. . Builder là một mẫu thiết kế thuộc nhóm Creational Pattern – những mẫu thiết kế cho việc khởi tạo đối tượng của lớp.

0 0 45

- vừa được xem lúc

Abstract Factory Design Pattern - Trợ thủ đắc lực của Developers

1. Giới thiệu. . Abstract Factory (Kit) là một design pattern thuộc nhóm Creational Pattern Design – những mẫu thiết kế cho việc khởi tạo đối tượng của lớp.

0 0 75