Facade design pattern

Facade design pattern:

The facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem. It acts as a front door to the subsystem, hiding the complexities and making it easier for clients to interact with it.

Key components of the Facade design pattern:

Facade: This is the primary interface through which clients interact. It encapsulates the interactions between the client and the subsystem, providing a simplified interface.

Subsystem: This refers to a set of classes or components that perform the actual work or contain the functionality needed by the client.

Benefits of facade design pattern:

  • Reduced learning curve: Clients don’t need to understand the intricacies of the subsystem to use it, making it easier for developers to integrate it into their applications.
  • Improved readability: The facade pattern helps to decouple the client code from the subsystem implementation, making the code more readable and understandable.
  • Simplified testing: Testing the client code becomes simpler without the need to deal with the internal complexities of the subsystem.
  • Encapsulation and flexibility: The facade controls access to the subsystem, ensuring that clients only interact with the intended functionalities. This allows for easier modification of the subsystem without affecting client code.

Usage of facade design pattern:

The following are some common usage:

  • When a subsystem is large and complex. The Facade pattern can provide a simpler way to access the subsystem’s functionality, making it easier for clients to use.
  • When a subsystem has many interdependent classes. The Facade pattern can simplify the client’s code by hiding the complexities of the subsystem’s object graph.
  • When a subsystem is poorly designed or undocumented. The Facade pattern can provide a cleaner and more consistent interface to the subsystem, even if its implementation is not ideal.
  • When you want to integrate with a third-party library or API. The Facade pattern can provide a more familiar interface to the third-party code, making it easier for your clients to use.

The following are some real world usage:

  • The Java AWT and Swing libraries provide facades for creating and managing graphical user interfaces. These facades abstract away the complexities of the underlying graphics code, making it easier for programmers to create user interfaces.
  • The Java JDBC API provides a facade for accessing databases. This facade simplifies the process of connecting to databases, executing queries, and retrieving results.
  • The Google Maps API provides a facade for embedding maps into web applications. This facade simplifies the process of creating maps, adding markers, and customizing the map’s appearance.

Note about facade design pattern:

  • The Facade pattern helps in utilizing the Single Responsibility Principle by providing a simplified interface to a complex system.
  • The Facade pattern promotes the Principle of Least Knowledge(Law of Demeter) by reducing the dependencies between client code and the subsystem’s classes.
  • Open/Closed Principle (OCP) and Interface Segregation Principle (ISP) might indirectly benefit from the use of the Facade pattern

Example of facade design pattern:

Suppose we have a complex subsystem for ordering a product, involving multiple classes:

InventoryService – Checks if the product is in stock.
PaymentService – Processes the payment.
ShippingService – Handles the shipping of the product.
The Facade class will simplify these interactions for clients by providing a single, unified interface to handle product orders.

// Subsystem classes
// InventoryService - checks if the product is available in stock
class InventoryService {
    public boolean checkStock(String productId) {
        // Logic to check stock
        System.out.println("Checking stock for product: " + productId);
        return true;  // Assume the product is in stock
    }
}

// PaymentService - processes the payment
class PaymentService {
    public boolean processPayment(String accountId, double amount) {
        // Logic to process payment
        System.out.println("Processing payment for account: " + accountId + ", amount: " + amount);
        return true;  // Assume payment is successful
    }
}

// ShippingService - handles shipping of the product
class ShippingService {
    public void shipProduct(String productId, String shippingAddress) {
        // Logic to ship the product
        System.out.println("Shipping product: " + productId + " to address: " + shippingAddress);
    }
}


// Facade
// OrderFacade - provides a simplified interface to place an order
class OrderFacade {
    private InventoryService inventoryService;
    private PaymentService paymentService;
    private ShippingService shippingService;

    public OrderFacade() {
        this.inventoryService = new InventoryService();
        this.paymentService = new PaymentService();
        this.shippingService = new ShippingService();
    }

    public void placeOrder(String productId, String accountId, double amount, String shippingAddress) {
        if (inventoryService.checkStock(productId)) {
            if (paymentService.processPayment(accountId, amount)) {
                shippingService.shipProduct(productId, shippingAddress);
                System.out.println("Order placed successfully!");
            } else {
                System.out.println("Payment failed.");
            }
        } else {
            System.out.println("Product is out of stock.");
        }
    }
}


// Client code
public class FacadePatternExample {
    public static void main(String[] args) {
        // Client code
        OrderFacade orderFacade = new OrderFacade();
        
        // Place an order using a simplified interface
        orderFacade.placeOrder("P123", "A456", 100.0, "123 Main St, Springfield");
    }
}