Facade pattern provides a simplified interface to a library, a framework, or any complex set of classes. It encapsulates complex subsystems for client to use without needing to understand their inner working.

Applicability

  • You have complex subsystems with multiple classes or interfaces, you want to provide a simplified and unified interface to interact with subsystems. Facade helps in reducing the complexity of using that subsystems by providing an entry point to them.
  • You want to simplify client code and abstract away the details and intricacies of the subsystem. Client can interact with the subsystem without needing to know its internal working.
  • You want to achieve loose coupling between the client code and the subsystem and avoid dependencies between them.

Pros and cons

Advantages

  • Reduce dealing with complexities on the client side
    • Shield the client from the subsystem components.
    • Simplify the usage of existing subsystems
  • Promote weak coupling between subsystems and client
    • Modify the subsystem components without affecting the client code
  • Ease of testing
    • One entry point of access to the complex subsystems allows less effort during unit testing without going through the entire subsystem.

Drawbacks

  • Might be bloated with methods
    • The facade might incorporate too much methods if not carefully designed, which could lead to the violation of single responsibility principle.
  • Might violate the OCP
    • When subsystems changes significantly or some new extensions are added to them, the facade might requires corresponding changes to keep it in sync, which introduces challenges relates to the open/closed principle.

Best practices

  • Keep the facade focused with clear responsibility, if necessary, it should provide simplified and cohesive interface only to a specific subset within the subsystem.
  • Design the facade and subsystem open for extension but closed for modification. Use interface, abstract classes and inheritance judiciously.

Approach

  1. Identify the complex subsystem in your application that could benefit from using a simplified interface. The subsystem might compose a set of classes work together to achieve a specific goal but might be challenging or sophisticated for client to interact with.
  2. Design the facade class that will provide a single point of access to the subsystem, representing well-defined subset of the subsystem’s functionalities.
  3. In the facade class, create instances or references to the subsystem components depending on the specific use case of the facade class
  4. Implement facade methods by delegating the work to the appropriate subsystem components.
  5. Consider multiple facade classes when necessary to enforce the OCP.

Components

  • Subsystem classes
    • Implement subsystem functionality.
    • Handle work assigned by facade class.
    • Have no knowledge about the facade, subsystem classes have no reference to the facade class.
  • Facade
    • Knows which subsystem classes are responsible for a request.
    • Delegates client requests to the appropriate subsystem objects.

Example

A bank client wants to withdraw and cash from the bank. The bank subsystem is responsible for displaying welcome message, validate client bank account number, validate security code, and manage transaction.

Subsystem classes

Welcome class

The welcome class displays welcome message when the client access the bank.

public class WelcomeToBank {
	public welcomeToBank() {
		System.out.println("Welcome to the ABC bank.");
	}
}

Account number checker class

The class check whether the account number the client provided matches the recorded account number.

public class AccountNumberChecker {
	private int accountNumber = 12345;
	public int getAccountNumber() {
		return accountNumber;
	}
	public boolean accountActive(int accountNumToCheck) {
		if (accountNumToCheck == getAccountNumber()) {
			return true;
		} else {
			return false;
		}
	}
}

Security code check class

The class checks the pass word the client provides against the record.

public class SecurityCodeChecker {
	private int securityCode = 1234;
		public int getSecurityCode() {
		return securityCode;
	}
	public boolean isCodeCorrect(int codeToCheck) {
		if (codeToCheck == getSecurityCode()) {
			return true;
		} else {
			return false;
		}
	}
}

Manage transaction class

This class checks the account balance and decide whether the balance is sufficient given the withdraw amount.

public class TransactionManager {
	private double accountBalance = 100.0;
	public double getBalance() {
		return accountBalance;
	}
	public withDrawCash(double withdrawAmount) {
		if (haveEnoughBalance(withdrawAmount)) {
			accountBalance -= withDrawAmount;
		} else {
			System.out.println("Insufficient balance!")
		}
	}
	public boolean haveEnoughBalance(double withdrawAmount) {
		if (withdrawAmount > getBalance()) {
			return false;
		} else {
			return true;
		}
	}
}

Facade

A simplified interface to the bank system, which includes component: AccountNumberChecker, SecurityCodeChecker, and TransactionManager.

public BankAccountFacade() {
	private int accountNumber;
	private int securityCode;
	
	// maintain references to necessary subsystem components
	AccountNumberChecker acctChecker;
	SecurityCodeChecker codeChecker;
	TransactionManager transacManager;
	WelcomeToBank bankWelcome;
	
	// the constructor initialise the necessary components of the bank system
	// and takes parameters that the components need
	public BacnkAccountFacade(int newAcctNum, int newSceCode) {
		accountNumber = newAcctNum;
		securityCode = newSecCode;
		bankWelcome = new WelcomeToBank();
		accChecker = new AccoutnNumberChecker();
		codeChecker = new SecurityCodeChecker();
		transacManager = new TransactionManager();
	}
	
	public int getACcountNumber() { return accoutNumber; }
	public int getSecurityCode() { return securitycCode; }
	
	// this method is the main entry point for clients to use the service of the 
	// bank, the facade coordiantes steps needed for the withdraw operation
	public void withdrawCash(double cashToGet) {
		// delegates the operations to appropriate subsystem objects
		if (acctChecker.accoutnActive(getACcountNumber()) &&
		codeChecker.isCodeCorrect(getSecurityCode()) &&
		transacManager.haveEnoughBalance(cashToGet)) {
			System.out.println("Transaction Completed!");
		} else {
			System.out.println("Transaction Failed.");
		}
	}
}

Client

The client does not know the inner working of the subsystem, it interacts with the subsystem via the facade object to access the services of the subsystem.

public class BankClient {
	public static void main(String[] args) {
		// client provides necessary parameters to the facade
		BankAccountFacade accessingBank = new BankAccountFacade(12345, 1234);
		accessingBank.withdrawCash(50.0);
		accessingBank.withdrawCash(200.0);
	}
}
OUTPUT:
Welcome to the ABC bank.
Transaction Completed!
Transaction Failed.

Design_PatternStructural_Design_PatternsFacade_patternSOFT2201