Facade Pattern
1. Giới Thiệu
1.1 Định Nghĩa
Facade Pattern là một structural pattern cung cấp một interface đơn giản cho một hệ thống con phức tạp. Nó định nghĩa một interface cao cấp giúp client dễ dàng sử dụng hệ thống con.
1.2 Mục Đích
- Đơn giản hóa interface phức tạp
- Giảm sự phụ thuộc của client vào các subsystem
- Cung cấp entry point thống nhất cho một tầng của phần mềm
- Đóng gói logic nghiệp vụ phức tạp
2. Cấu Trúc Cơ Bản
2.1 Computer System Example
// Subsystem components
public class CPU {
public void freeze() {
System.out.println("CPU: Freezing...");
}
public void jump(long position) {
System.out.println("CPU: Jumping to position " +
position);
}
public void execute() {
System.out.println("CPU: Executing...");
}
}
public class Memory {
public void load(long position, String data) {
System.out.println("Memory: Loading data at " +
position + ": " + data);
}
}
public class HardDrive {
public String read(long lba, int size) {
System.out.println("HardDrive: Reading " +
size + " bytes from " + lba);
return "Data from sector " + lba;
}
}
// Facade
public class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
public void start() {
cpu.freeze();
String bootData = hardDrive.read(
BOOT_SECTOR, SECTOR_SIZE);
memory.load(BOOT_ADDRESS, bootData);
cpu.jump(BOOT_ADDRESS);
cpu.execute();
}
private static final long BOOT_ADDRESS = 0x0;
private static final long BOOT_SECTOR = 0x0;
private static final int SECTOR_SIZE = 512;
}
// Client
public class Client {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
3. Ví Dụ Thực Tế
3.1 Payment Processing System
// Subsystem components
public class PaymentValidator {
public boolean validate(Payment payment) {
// Validation logic
return true;
}
}
public class FraudDetector {
public boolean checkFraud(Payment payment) {
// Fraud detection logic
return false;
}
}
public class PaymentProcessor {
public void process(Payment payment) {
// Processing logic
}
}
public class NotificationService {
public void notify(String message) {
// Notification logic
}
}
// Facade
@Service
public class PaymentFacade {
private final PaymentValidator validator;
private final FraudDetector fraudDetector;
private final PaymentProcessor processor;
private final NotificationService notifier;
public PaymentFacade(
PaymentValidator validator,
FraudDetector fraudDetector,
PaymentProcessor processor,
NotificationService notifier) {
this.validator = validator;
this.fraudDetector = fraudDetector;
this.processor = processor;
this.notifier = notifier;
}
public PaymentResult processPayment(Payment payment) {
try {
// Validate payment
if (!validator.validate(payment)) {
return PaymentResult.invalid(
"Invalid payment details");
}
// Check for fraud
if (fraudDetector.checkFraud(payment)) {
return PaymentResult.fraud(
"Suspicious activity detected");
}
// Process payment
processor.process(payment);
// Notify success
notifier.notify("Payment processed successfully");
return PaymentResult.success();
} catch (Exception e) {
notifier.notify("Payment processing failed");
return PaymentResult.error(e.getMessage());
}
}
}
3.2 Order Management System
// Subsystem components
public class InventoryService {
public boolean checkStock(String productId, int quantity) {
// Check stock logic
return true;
}
public void updateStock(String productId, int quantity) {
// Update stock logic
}
}
public class PricingService {
public BigDecimal calculatePrice(
String productId, int quantity) {
// Price calculation logic
return BigDecimal.TEN;
}
}
public class ShippingService {
public ShippingDetails arrangeShipping(Order order) {
// Shipping arrangement logic
return new ShippingDetails();
}
}
public class PaymentService {
public boolean processPayment(
Order order, BigDecimal amount) {
// Payment processing logic
return true;
}
}
// Facade
@Service
public class OrderFacade {
private final InventoryService inventoryService;
private final PricingService pricingService;
private final ShippingService shippingService;
private final PaymentService paymentService;
public OrderFacade(
InventoryService inventoryService,
PricingService pricingService,
ShippingService shippingService,
PaymentService paymentService) {
this.inventoryService = inventoryService;
this.pricingService = pricingService;
this.shippingService = shippingService;
this.paymentService = paymentService;
}
public OrderResult placeOrder(Order order) {
// Check inventory
if (!checkInventory(order)) {
return OrderResult.outOfStock();
}
// Calculate total price
BigDecimal totalPrice = calculateTotalPrice(order);
// Process payment
if (!paymentService.processPayment(
order, totalPrice)) {
return OrderResult.paymentFailed();
}
// Update inventory
updateInventory(order);
// Arrange shipping
ShippingDetails shipping =
shippingService.arrangeShipping(order);
return OrderResult.success(shipping);
}
private boolean checkInventory(Order order) {
return order.getItems().stream()
.allMatch(item ->
inventoryService.checkStock(
item.getProductId(),
item.getQuantity()));
}
private BigDecimal calculateTotalPrice(Order order) {
return order.getItems().stream()
.map(item -> pricingService.calculatePrice(
item.getProductId(),
item.getQuantity()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private void updateInventory(Order order) {
order.getItems().forEach(item ->
inventoryService.updateStock(
item.getProductId(),
-item.getQuantity()));
}
}
4. Spring Framework Integration
4.1 Configuration
@Configuration
public class FacadeConfig {
@Bean
public PaymentFacade paymentFacade(
PaymentValidator validator,
FraudDetector fraudDetector,
PaymentProcessor processor,
NotificationService notifier) {
return new PaymentFacade(
validator, fraudDetector,
processor, notifier);
}
@Bean
public OrderFacade orderFacade(
InventoryService inventoryService,
PricingService pricingService,
ShippingService shippingService,
PaymentService paymentService) {
return new OrderFacade(
inventoryService, pricingService,
shippingService, paymentService);
}
}
4.2 REST Controller
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderFacade orderFacade;
public OrderController(OrderFacade orderFacade) {
this.orderFacade = orderFacade;
}
@PostMapping
public ResponseEntity<OrderResult> placeOrder(
@RequestBody Order order) {
OrderResult result = orderFacade.placeOrder(order);
if (result.isSuccess()) {
return ResponseEntity.ok(result);
} else {
return ResponseEntity
.badRequest()
.body(result);
}
}
}
5. Testing Facade Pattern
5.1 Unit Testing
@ExtendWith(MockitoExtension.class)
class OrderFacadeTest {
@Mock
private InventoryService inventoryService;
@Mock
private PricingService pricingService;
@Mock
private ShippingService shippingService;
@Mock
private PaymentService paymentService;
@InjectMocks
private OrderFacade orderFacade;
@Test
void whenPlaceOrder_thenSuccess() {
// Given
Order order = createTestOrder();
when(inventoryService.checkStock(
anyString(), anyInt()))
.thenReturn(true);
when(pricingService.calculatePrice(
anyString(), anyInt()))
.thenReturn(BigDecimal.TEN);
when(paymentService.processPayment(
any(), any()))
.thenReturn(true);
when(shippingService.arrangeShipping(any()))
.thenReturn(new ShippingDetails());
// When
OrderResult result = orderFacade.placeOrder(order);
// Then
assertTrue(result.isSuccess());
verify(inventoryService).updateStock(
anyString(), anyInt());
}
@Test
void whenOutOfStock_thenFailure() {
// Given
Order order = createTestOrder();
when(inventoryService.checkStock(
anyString(), anyInt()))
.thenReturn(false);
// When
OrderResult result = orderFacade.placeOrder(order);
// Then
assertFalse(result.isSuccess());
assertEquals("Out of stock", result.getMessage());
}
}
5.2 Integration Testing
@SpringBootTest
class OrderFacadeIntegrationTest {
@Autowired
private OrderFacade orderFacade;
@Test
void whenPlaceOrder_thenSuccess() {
// Given
Order order = createTestOrder();
// When
OrderResult result = orderFacade.placeOrder(order);
// Then
assertTrue(result.isSuccess());
assertNotNull(result.getShippingDetails());
}
}
6. Lợi Ích và Nhược Điểm
6.1 Lợi Ích
- Giảm sự phức tạp cho client
- Giảm sự phụ thuộc giữa client và subsystem
- Tăng tính bảo mật và kiểm soát
- Dễ dàng thay đổi subsystem
- Cải thiện khả năng bảo trì
6.2 Nhược Điểm
- Có thể trở thành God object
- Khó mở rộng khi có nhiều subsystem
- Có thể vi phạm Single Responsibility Principle
- Khó tái sử dụng các thành phần riêng lẻ
7. Best Practices
7.1 Interface Segregation
public interface OrderProcessing {
OrderResult placeOrder(Order order);
}
public interface OrderManagement {
Order getOrder(String orderId);
void cancelOrder(String orderId);
}
public interface OrderReporting {
List<Order> getOrderHistory(String customerId);
OrderStatistics getStatistics(DateRange range);
}
@Service
public class OrderFacade implements
OrderProcessing,
OrderManagement,
OrderReporting {
// Implementation
}
7.2 Builder Pattern Integration
public class OrderFacadeBuilder {
private InventoryService inventoryService;
private PricingService pricingService;
private ShippingService shippingService;
private PaymentService paymentService;
public OrderFacadeBuilder withInventoryService(
InventoryService service) {
this.inventoryService = service;
return this;
}
public OrderFacadeBuilder withPricingService(
PricingService service) {
this.pricingService = service;
return this;
}
public OrderFacadeBuilder withShippingService(
ShippingService service) {
this.shippingService = service;
return this;
}
public OrderFacadeBuilder withPaymentService(
PaymentService service) {
this.paymentService = service;
return this;
}
public OrderFacade build() {
validateServices();
return new OrderFacade(
inventoryService,
pricingService,
shippingService,
paymentService);
}
private void validateServices() {
if (inventoryService == null ||
pricingService == null ||
shippingService == null ||
paymentService == null) {
throw new IllegalStateException(
"All services must be provided");
}
}
}
8. Common Issues và Solutions
8.1 Error Handling
public class OrderFacade {
private final ErrorHandler errorHandler;
public OrderResult placeOrder(Order order) {
try {
// Normal order processing
return processOrder(order);
} catch (InventoryException e) {
return errorHandler.handleInventoryError(e);
} catch (PaymentException e) {
return errorHandler.handlePaymentError(e);
} catch (ShippingException e) {
return errorHandler.handleShippingError(e);
} catch (Exception e) {
return errorHandler.handleUnexpectedError(e);
}
}
}
@Component
public class ErrorHandler {
private final NotificationService notifier;
private final Logger logger;
public OrderResult handleInventoryError(
InventoryException e) {
logger.error("Inventory error", e);
notifier.notifyAdmin(
"Inventory error: " + e.getMessage());
return OrderResult.error(
"Product not available");
}
public OrderResult handlePaymentError(
PaymentException e) {
logger.error("Payment error", e);
return OrderResult.error(
"Payment processing failed");
}
// Other error handling methods
}
8.2 Performance Optimization
public class CachingOrderFacade extends OrderFacade {
private final Cache<String, BigDecimal> priceCache;
private final Cache<String, Boolean> stockCache;
public CachingOrderFacade(
InventoryService inventoryService,
PricingService pricingService,
ShippingService shippingService,
PaymentService paymentService) {
super(inventoryService, pricingService,
shippingService, paymentService);
this.priceCache = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofMinutes(5))
.maximumSize(1000)
.build();
this.stockCache = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(30))
.maximumSize(1000)
.build();
}
@Override
protected BigDecimal calculateTotalPrice(Order order) {
return order.getItems().stream()
.map(item -> getPriceFromCache(
item.getProductId(),
item.getQuantity()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private BigDecimal getPriceFromCache(
String productId, int quantity) {
String cacheKey = productId + ":" + quantity;
return priceCache.get(cacheKey, key ->
super.getPricingService().calculatePrice(
productId, quantity));
}
}
9. References
9.1 Books
- Design Patterns: Elements of Reusable Object-Oriented Software
- Head First Design Patterns
- Clean Architecture by Robert C. Martin
- Patterns of Enterprise Application Architecture