Factory Pattern
1. Giới Thiệu
1.1 Định Nghĩa
Factory Pattern là một creational pattern cung cấp interface để tạo đối tượng trong superclass, nhưng cho phép subclasses thay đổi kiểu đối tượng sẽ được tạo.
1.2 Các Loại Factory Pattern
- Simple Factory
- Factory Method
- Abstract Factory
2. Simple Factory
2.1 Cấu Trúc
// Product interface
public interface Animal {
void makeSound();
}
// Concrete products
public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
// Simple factory
public class AnimalFactory {
public Animal createAnimal(String type) {
if ("dog".equalsIgnoreCase(type)) {
return new Dog();
} else if ("cat".equalsIgnoreCase(type)) {
return new Cat();
}
throw new IllegalArgumentException("Unknown animal type");
}
}
2.2 Sử Dụng
public class Client {
public static void main(String[] args) {
AnimalFactory factory = new AnimalFactory();
Animal dog = factory.createAnimal("dog");
dog.makeSound(); // Output: Woof!
Animal cat = factory.createAnimal("cat");
cat.makeSound(); // Output: Meow!
}
}
3. Factory Method
3.1 Cấu Trúc
// Creator
public abstract class PaymentProcessor {
public abstract Payment createPayment();
public void processPayment() {
Payment payment = createPayment();
payment.validate();
payment.process();
}
}
// Concrete creators
public class CreditCardProcessor extends PaymentProcessor {
@Override
public Payment createPayment() {
return new CreditCardPayment();
}
}
public class PayPalProcessor extends PaymentProcessor {
@Override
public Payment createPayment() {
return new PayPalPayment();
}
}
// Product interface
public interface Payment {
void validate();
void process();
}
// Concrete products
public class CreditCardPayment implements Payment {
@Override
public void validate() {
System.out.println("Validating credit card...");
}
@Override
public void process() {
System.out.println("Processing credit card payment...");
}
}
public class PayPalPayment implements Payment {
@Override
public void validate() {
System.out.println("Validating PayPal account...");
}
@Override
public void process() {
System.out.println("Processing PayPal payment...");
}
}
3.2 Sử Dụng
public class Client {
public static void main(String[] args) {
PaymentProcessor processor = new CreditCardProcessor();
processor.processPayment();
processor = new PayPalProcessor();
processor.processPayment();
}
}
4. Abstract Factory
4.1 Cấu Trúc
// Abstract factory
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// Concrete factories
public class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
public class MacFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
// Abstract products
public interface Button {
void paint();
}
public interface Checkbox {
void paint();
}
// Concrete products
public class WindowsButton implements Button {
@Override
public void paint() {
System.out.println("Rendering Windows button");
}
}
public class MacButton implements Button {
@Override
public void paint() {
System.out.println("Rendering Mac button");
}
}
public class WindowsCheckbox implements Checkbox {
@Override
public void paint() {
System.out.println("Rendering Windows checkbox");
}
}
public class MacCheckbox implements Checkbox {
@Override
public void paint() {
System.out.println("Rendering Mac checkbox");
}
}
4.2 Sử Dụng
public class Application {
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void paint() {
button.paint();
checkbox.paint();
}
}
public class Client {
public static void main(String[] args) {
Application app;
app = new Application(new WindowsFactory());
app.paint();
app = new Application(new MacFactory());
app.paint();
}
}
5. Ví Dụ Thực Tế
5.1 Database Connection Factory
public interface DatabaseConnection {
void connect();
void disconnect();
void executeQuery(String query);
}
public class MySQLConnection implements DatabaseConnection {
@Override
public void connect() {
System.out.println("Connecting to MySQL...");
}
@Override
public void disconnect() {
System.out.println("Disconnecting from MySQL...");
}
@Override
public void executeQuery(String query) {
System.out.println("Executing MySQL query: " + query);
}
}
public class PostgreSQLConnection implements DatabaseConnection {
@Override
public void connect() {
System.out.println("Connecting to PostgreSQL...");
}
@Override
public void disconnect() {
System.out.println("Disconnecting from PostgreSQL...");
}
@Override
public void executeQuery(String query) {
System.out.println("Executing PostgreSQL query: " + query);
}
}
public class DatabaseFactory {
public static DatabaseConnection createConnection(String type) {
if ("mysql".equalsIgnoreCase(type)) {
return new MySQLConnection();
} else if ("postgresql".equalsIgnoreCase(type)) {
return new PostgreSQLConnection();
}
throw new IllegalArgumentException("Unknown database type");
}
}
5.2 Document Generator Factory
public interface Document {
void generate();
void save();
}
public class PDFDocument implements Document {
@Override
public void generate() {
System.out.println("Generating PDF document...");
}
@Override
public void save() {
System.out.println("Saving PDF document...");
}
}
public class WordDocument implements Document {
@Override
public void generate() {
System.out.println("Generating Word document...");
}
@Override
public void save() {
System.out.println("Saving Word document...");
}
}
public abstract class DocumentFactory {
public abstract Document createDocument();
public void processDocument() {
Document doc = createDocument();
doc.generate();
doc.save();
}
}
public class PDFFactory extends DocumentFactory {
@Override
public Document createDocument() {
return new PDFDocument();
}
}
public class WordFactory extends DocumentFactory {
@Override
public Document createDocument() {
return new WordDocument();
}
}
6. Lợi Ích và Nhược Điểm
6.1 Lợi Ích
- Loose coupling giữa creator và concrete products
- Single Responsibility Principle
- Open/Closed Principle
- Dễ dàng mở rộng code
- Tập trung logic tạo đối tượng
6.2 Nhược Điểm
- Code có thể trở nên phức tạp
- Cần tạo nhiều class mới
- Khó debug với factory methods
- Khó thay đổi toàn bộ product family
7. Best Practices
7.1 Dependency Injection
@Configuration
public class FactoryConfig {
@Bean
public PaymentFactory paymentFactory() {
Map<String, Payment> paymentMap = new HashMap<>();
paymentMap.put("credit", new CreditCardPayment());
paymentMap.put("paypal", new PayPalPayment());
return new PaymentFactory(paymentMap);
}
}
@Service
public class PaymentService {
private final PaymentFactory paymentFactory;
@Autowired
public PaymentService(PaymentFactory paymentFactory) {
this.paymentFactory = paymentFactory;
}
public void processPayment(String type) {
Payment payment = paymentFactory.createPayment(type);
payment.process();
}
}
7.2 Exception Handling
public class FactoryException extends RuntimeException {
public FactoryException(String message) {
super(message);
}
public FactoryException(String message, Throwable cause) {
super(message, cause);
}
}
public class PaymentFactory {
private final Map<String, Payment> paymentMap;
public PaymentFactory(Map<String, Payment> paymentMap) {
this.paymentMap = paymentMap;
}
public Payment createPayment(String type) {
Payment payment = paymentMap.get(type);
if (payment == null) {
throw new FactoryException(
"Unknown payment type: " + type);
}
return payment;
}
}
8. Testing
8.1 Unit Testing
@ExtendWith(MockitoExtension.class)
class PaymentFactoryTest {
@InjectMocks
private PaymentFactory paymentFactory;
@Mock
private Map<String, Payment> paymentMap;
@Test
void whenValidType_thenCreatePayment() {
// Given
String type = "credit";
Payment payment = mock(Payment.class);
when(paymentMap.get(type)).thenReturn(payment);
// When
Payment result = paymentFactory.createPayment(type);
// Then
assertNotNull(result);
assertSame(payment, result);
}
@Test
void whenInvalidType_thenThrowException() {
// Given
String type = "invalid";
when(paymentMap.get(type)).thenReturn(null);
// When/Then
assertThrows(FactoryException.class, () ->
paymentFactory.createPayment(type));
}
}
8.2 Integration Testing
@SpringBootTest
class PaymentServiceIntegrationTest {
@Autowired
private PaymentService paymentService;
@Test
void whenProcessPayment_thenSuccess() {
// Given
String type = "credit";
// When/Then
assertDoesNotThrow(() ->
paymentService.processPayment(type));
}
@Test
void whenProcessInvalidPayment_thenThrowException() {
// Given
String type = "invalid";
// When/Then
assertThrows(FactoryException.class, () ->
paymentService.processPayment(type));
}
}
9. References
9.1 Books
- Design Patterns: Elements of Reusable Object-Oriented Software
- Head First Design Patterns
- Clean Code
- Effective Java