Паттерн Фасад (Facade Pattern) предоставляет упрощенный унифицированный интерфейс для сложной подсистемы классов. Фасад скрывает сложность взаимодействия между множеством классов и предоставляет клиенту простой интерфейс для выполнения сложных операций. Это упрощает использование подсистемы и делает код более читаемым и поддерживаемым.
В примере ниже класс OrderFacade выступает фасадом для сложной подсистемы, состоящей из классов Inventory,
Payment и Shipping. Вместо того чтобы клиент напрямую взаимодействовал с каждым классом подсистемы, он использует
простой метод placeOrder() фасада, который координирует работу всех компонентов системы.
// Подсистема: Управление складом
class Inventory {
checkAvailability(productId) {
console.log(`Checking availability for product ${productId}...`);
// Имитация проверки наличия товара
return Math.random() > 0.2; // 80% вероятность наличия
}
reserveProduct(productId, quantity) {
console.log(`Reserving ${quantity} units of product ${productId}`);
return { reserved: true, productId, quantity };
}
releaseReservation(productId) {
console.log(`Releasing reservation for product ${productId}`);
}
}
// Подсистема: Обработка платежей
class Payment {
processPayment(amount, paymentMethod) {
console.log(`Processing payment of $${amount} via ${paymentMethod}...`);
// Имитация обработки платежа
const success = Math.random() > 0.1; // 90% вероятность успеха
if (success) {
console.log(`Payment successful! Transaction ID: ${Math.random().toString(36).substr(2, 9)}`);
return { success: true, transactionId: Math.random().toString(36).substr(2, 9) };
} else {
console.log('Payment failed!');
return { success: false };
}
}
refundPayment(transactionId) {
console.log(`Refunding payment for transaction ${transactionId}`);
return { refunded: true };
}
}
// Подсистема: Доставка
class Shipping {
createShipment(orderId, address) {
console.log(`Creating shipment for order ${orderId} to ${address}`);
const trackingNumber = `TRACK-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
console.log(`Shipment created! Tracking number: ${trackingNumber}`);
return { trackingNumber, status: 'created' };
}
updateShipmentStatus(trackingNumber, status) {
console.log(`Updating shipment ${trackingNumber} status to: ${status}`);
}
}
// Фасад: Упрощенный интерфейс для работы с подсистемой
class OrderFacade {
constructor() {
this.inventory = new Inventory();
this.payment = new Payment();
this.shipping = new Shipping();
}
// Простой метод для размещения заказа
placeOrder(productId, quantity, amount, paymentMethod, address) {
console.log('=== Placing Order ===');
// 1. Проверка наличия товара
if (!this.inventory.checkAvailability(productId)) {
console.log('Order failed: Product not available');
return { success: false, reason: 'Product not available' };
}
// 2. Резервирование товара
const reservation = this.inventory.reserveProduct(productId, quantity);
// 3. Обработка платежа
const paymentResult = this.payment.processPayment(amount, paymentMethod);
if (!paymentResult.success) {
// Отмена резервирования при неудачном платеже
this.inventory.releaseReservation(productId);
console.log('Order failed: Payment failed');
return { success: false, reason: 'Payment failed' };
}
// 4. Создание доставки
const orderId = `ORD-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
const shipment = this.shipping.createShipment(orderId, address);
console.log('=== Order Placed Successfully ===');
return {
success: true,
orderId,
reservation,
payment: paymentResult,
shipment
};
}
// Метод для отмены заказа
cancelOrder(orderId, productId, transactionId) {
console.log(`=== Canceling Order ${orderId} ===`);
this.inventory.releaseReservation(productId);
this.payment.refundPayment(transactionId);
console.log('Order canceled successfully');
return { canceled: true };
}
}
// Примеры использования
const orderFacade = new OrderFacade();
// Простое размещение заказа через фасад
const order1 = orderFacade.placeOrder(
'PROD-123',
2,
150.00,
'credit-card',
'123 Main St, City'
);
console.log('\nOrder details:', order1);
// Отмена заказа
if (order1.success) {
orderFacade.cancelOrder(
order1.orderId,
'PROD-123',
order1.payment.transactionId
);
}
// Без фасада клиенту пришлось бы делать так:
console.log('\n=== Without Facade (Complex) ===');
const inventory = new Inventory();
const payment = new Payment();
const shipping = new Shipping();
if (inventory.checkAvailability('PROD-456')) {
const reservation = inventory.reserveProduct('PROD-456', 1);
const paymentResult = payment.processPayment(75.00, 'paypal');
if (paymentResult.success) {
const orderId = 'ORD-789';
shipping.createShipment(orderId, '456 Oak Ave');
console.log('Order completed manually');
} else {
inventory.releaseReservation('PROD-456');
}
}