Skip to content

Facade

The facade is another design pattern where the main purpose is to provide an interface that:

  • simplifies the performance of a certain operation by taking a few steps that are required to obtain a certain result. Most often, these steps are method calls on separate objects.
  • integrates many interfaces into one common one e.g. used by the end-user of a given library.

The use of the facade involves the use of business related objects that have been divided, e.g. according to the SOLID principles (above all the Single Responsibility principle).

Example 1

The example shows how to create a simple facade to perform the ordering operation. Such an operation requires:

  • to check if the ordered product is available
  • to pay for the product
  • to deliver the product

facade

An appropriate interface is responsible for each of the above operations:

  • DeliveryService
  • PaymentService
  • ProductAvailabilityService

Facade uses these three interfaces (respecting the SOLID principle - dependency injection) to perform an operation with a single method call:

public interface DeliveryService {
  void deliverProduct(Long productsId, int amount, String recipient);
}
public interface PaymentService {
  void pay(Long productId, int amount);
}
public interface ProductAvailabilityService {
  boolean isAvailable(Long productId);
}
public class OrderFacade {
  private final DeliveryService deliveryService;
  private final PaymentService paymentService;
  private final ProductAvailabilityService productAvailabilityService;

  public OrderFacade(final DeliveryService deliveryService, final PaymentService paymentService, final ProductAvailabilityService productAvailabilityService) {
    this.deliveryService = deliveryService;
    this.paymentService = paymentService;
    this.productAvailabilityService = productAvailabilityService;
  }

  public boolean placeOrder(final Long productId, final int amount, final String recipient) {
    if (productAvailabilityService.isAvailable(productId)) {
      paymentService.pay(productId, amount);
      deliveryService.deliverProduct(productId, amount, recipient);
      return true;
    }
    return false;
  }
}

Example 2

The following example shows a slightly different approach to the facade pattern.

facade

This example includes three implementations of the Encryptor interface, which hashes the inputString in some way. This interface has three implementations:

  • BCryptEncryptor - which (in real implementation) uses the BCrypt algorithm to hash input characters
  • SCryptEncryptor - which uses the SCrypt algorithm
  • NoOpEncryptor - which does not change input characters.

Instead, we want to "free" the end-user from using specific implementations of the Encryptor interface. We want to give the ability to use the Encryptors interface, which hashes the input according to the appropriate method chosen. EncryptionFacade is its implementation:

public interface Encryptor {
  String encrypt(String toEncrypt);
}
public class BCryptEncryptor implements Encryptor {
  @Override
  public String encrypt(final String toEncrypt) {
    return "encrypting " + toEncrypt + "with BCrypt";
  }
}
public class SCryptEncryptor implements Encryptor {
  @Override
  public String encrypt(final String toEncrypt) {
    return "encrypting " + toEncrypt + "with SCrypt";
  }
}
public class NoOpEncryptor implements Encryptor {
  @Override
  public String encrypt(final String toEncrypt) {
    return toEncrypt;
  }
}
public interface Encryptors {
  String encryptWithoutModification(final String toEncrypt);
  String encryptWithBCrypt(final String toEncrypt);
  String encryptWithSCrypt(final String toEncrypt);
}
public class EncryptionFacade implements Encryptors {
  private final SCryptEncryptor sCryptEncryptor;
  private final BCryptEncryptor bCryptEncryptor;
  private final NoOpEncryptor noOpEncryptor;

  public EncryptionFacade(final SCryptEncryptor sCryptEncryptor, final BCryptEncryptor bCryptEncryptor, final NoOpEncryptor noOpEncryptor) {
    this.sCryptEncryptor = sCryptEncryptor;
    this.bCryptEncryptor = bCryptEncryptor;
    this.noOpEncryptor = noOpEncryptor;
  }

  @Override
  public String encryptWithoutModification(final String toEncrypt) {
    return noOpEncryptor.encrypt(toEncrypt);
  }

  @Override
  public String encryptWithBCrypt(final String toEncrypt) {
    return bCryptEncryptor.encrypt(toEncrypt);
  }

  @Override
  public String encryptWithSCrypt(final String toEncrypt) {
    return sCryptEncryptor.encrypt(toEncrypt);
  }
}


In conclusion, both of the above examples show that the main purpose of using a facade is to reduce the complexity of operations performed on related objects or hide implementation details.