DEV Community

Nick Vasilev
Nick Vasilev

Posted on

Design Patterns: Facade

Introduction

The Facade design pattern serves as a front-facing interface for a complex set of classes, a library, or a framework. This article will explain its applications in iOS development and the types of problems it can solve.

Table of contents

Definition

The Facade design pattern is a structural pattern that provides a simplified interface to a framework, library, or a set of related classes.

What kind of problems can the Facade design pattern solve?

  1. Complexity of subsystems. Large systems often consist of multiple interconnected classes and components, making them difficult to use and understand. A client that interacts directly with multiple components may require extensive knowledge of their implementation details.

  2. Tight coupling. When clients are directly dependent on multiple components, changes in the subsystem can lead to widespread modifications in client code, increasing maintenance costs.

  3. Difficulties in migration or refactoring. If a system's implementation changes, all clients interacting with its components must be updated accordingly, leading to high development effort.

What solutions does the Facade design pattern describe?

  1. Provide a unified interface. The Facade design pattern introduces a higher-level interface that encapsulates complex interactions between multiple subsystem components, exposing a simple and easy-to-use API.

  2. Decouple the client from the subsystem. Clients only interact with the facade, reducing their dependencies on specific subsystem implementations. This improves maintainability and allows for modifications to the underlying system.

  3. Encapsulate complexity. The facade acts as a mediator, handling interactions between subsystem components internally, so clients do not need to manage multiple dependencies.

UML Diagram

UML Diagram

The Client does not access the subsystem components directly. Instead, the Client works through a Facade class that provides a simplified interface. The Client depends only on the Facade interface and doesn't know anything about the subsystem components or their relationships.

Examples

Let's imagine that the system has three classes:

  • AuthManager is responsible for the authentication process and handling all tasks related to authentication. For the sake of simplicity, it exposes only a single method: login(username:password).
  • PaymentProcessor is responsible for processing a payment transaction.
  • NotificationService is responsible for sending a notification to observers that the order has been placed.

If a client wants to work with these objects, it would need to communicate with each object directly, which leads to the problems described above. It's better to create a facade object that serves as a front-facing interface for the client and hides the communication complexity between objects.

protocol IAuthManager {
  func login(username: String, password: String)
}

final class AuthManager: IAuthManager {
  func login(username: String, password: String) {
    print("User logged in")
  }
}
Enter fullscreen mode Exit fullscreen mode
protocol IPaymentProcessor {
  func processPayment(amount: Double)
}

final class PaymentProcessor: IPaymentProcessor {
  func processPayment(amount: Double) { print("Payment processed: $\(amount)") }
}
Enter fullscreen mode Exit fullscreen mode
protocol INotificationService {
  func sendNotification(message: String)
}

final class NotificationService: INotificationService {
  func sendNotification(message: String) { print("Notification sent: \(message)") }
}
Enter fullscreen mode Exit fullscreen mode

Define an IECommerceFacade protocol that exposes a single method for placing an order, and an ECommerceFacade class that implements this protocol and sets up communication between subsystem objects, which are hidden from the client.

protocol IECommerceFacade {
  func placeOrder(username: String, password: String, amount: Double)
}

final class ECommerceFacade: IECommerceFacade {
    private let authManager: IAuthManager
    private let paymentProcessor: IPaymentProcessor
    private let notificationService: INotificationService

    init(authManager: IAuthManager, paymentProcessor: IPaymentProcessor, notificationService: INotificationService) {
      self.authManager = authManager
      self.paymentProcessor = paymentProcessor
      self.notificationService = notificationService
    }

    func placeOrder(username: String, password: String, amount: Double) {
        authManager.login(username: username, password: password)
        paymentProcessor.processPayment(amount: amount)
        notificationService.sendNotification(message: "Your order has been placed.")
    }
}
Enter fullscreen mode Exit fullscreen mode
let authManager = AuthManager()
let paymentProcessor = PaymentProcessor()
let noticationService = NotificationService()

let ecommerceFacade = ECommerceFacade(
  authManager: authManager, 
  paymentProcessor: paymentProcessor, 
  notificationService: notificationService
)

ecommerceFacade.placeOrder(username: "JohnDoe", password: "1234", amount: 99.99)
Enter fullscreen mode Exit fullscreen mode

This example demonstrates the Facade pattern by providing a single ECommerceFacade class that simplifies the process of placing an order, while internally managing authentication, payment, and notifications.

Summary

The Facade design pattern simplifies complex systems by providing a unified interface that encapsulates interactions between multiple subsystem components. It solves problems related to system complexity, tight coupling, and high maintenance costs by decoupling clients from the internal workings of the system. This pattern improves code readability, maintainability, and flexibility.

By adopting the Facade pattern, developers can create modular, maintainable, and user-friendly systems, making it an essential tool in software design and architecture.

Top comments (0)