Facade Design Pattern

Introduction

The Facade design pattern is a simple and widely used design pattern. We use the facade design pattern when we want to hide the system complexity from the client.

Facade Meaning: A facade is the outer layer of a complex system. It is like a front or appearance that may not necessarily reflect what is truly going on underneath.

Facade High-level View: The facade layer simply hides the complex system implementation and logics. The user interacts with the facade layer, and the facade layer interacts with complex systems. Note that the user can directly use any classes of the complex system

Explanation

Real World Usecase

Car: In a car, to increase the speed, we have the accelerator, and to decrease the speed, we have brakes. Internally, these two functionalities will interact with the car components (classes) and regulate the speed. The accelerator and brake functionalities serve as the facades, and the layer in which these two are present in the car is known as the facade layer.

Scenarios

Scenario 1: EmployeeDao can have many methods, approximately 40-50. In the facade layer, we expose only a few methods that are required by the client (DAO - Data Access Object). The following are sample code snippets. Please note that EmployeeFacade has an object of EmployeeDao, and its initialization is also done by the facade.

package dao;
import dto.Employee;

public class EmployeeDao {

    public void insert(){
        //insert employee details into the db
    }

    public void updateEmployeeName(int empId){
        //update employee details
    }

    public Employee getEmployeeDetails(String emailId){
        //return employee details based on emailId
        return new Employee();
    }

    public Employee getEmployeeDetails(int empId){
        //return employee details based on empId;
        return new Employee();
    }
}
package facade;

import dao.EmployeeDao;
import dto.Employee;

public class EmployeeFacade {
    EmployeeDao employeeDao;

    public EmployeeFacade() {
        this.employeeDao = new EmployeeDao();
    }

    public void insert(){
        employeeDao.insert();
    }

    public Employee getEmployeeDetails(int empId){
        return employeeDao.getEmployeeDetails(empId);
    }
}
package client;

import dto.Employee;
import facade.EmployeeFacade;

public class EmployeeClient {
    public void getEmployeeDetails(){
        EmployeeFacade facade = new EmployeeFacade();
        Employee employeeDetails = facade.getEmployeeDetails(12424);
    }
}

Scenario 2: When purchasing a product in the e-commerce domain, if the client directly communicates with the payment subsystem, there are two problems:

  1. If there is a new step in the order creation process, the client also needs to make modifications on its end.

  2. If there is any change in the existing logic of classes, there will be an impact on the client.

To mitigate all these issues, we introduce a facade layer where the client interacts. All the changes only impact the facade, and we make changes only in the facade layer.

Facade Layer

package Payments;

public class OrderFacade {

    ProductDao productDao;
    Invoice invoice;
    Payment payment;
    SendNotification sendNotification;

    public OrderFacade() {
        this.productDao = new ProductDao();
        this.invoice = new Invoice();
        this.payment = new Payment();
        this.sendNotification = new SendNotification();
    }

    public void createOrder(){
        Product product = productDao.getProduct(121);
        payment.makePayment();
        invoice.generateInvoice();
        sendNotification.sendNotification();
    }
}

Scenario 3

Facade can internally use multiple facades which can hide complex systems all together.

Facade vs Proxy

The proxy is responsible for a specific object; for example, in the EmployeeDao class, the proxy checks whether the user has the right permissions to perform certain operations. The proxy is designed to handle only one object, and its type corresponds to that of the object.

Facade vs Adapter

An adapter is used to ensure compatibility between the original interface and the supplier. The client cannot process data directly from the supplier. Hence, the adapter acts like a converter in between and sends the right type of data to the client.