Facade Pattern

Паттерн Фасад для упрощения интерфейса сложной подсистемы

Паттерн Фасад (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');
  }
}