Fork me on GitHub

Facade

Motivation

Let’s say that we need to develop a compiler for a brand new programming language.

The compiling process consist of steps, such as scanning, tokenizing, parsing, building abstract syntax tree, code generation, etc. We need to develop a separate subcomponent for each step. In principle, each subcomponent is complex, and the usage of subcomponents is complex as well.

It does not make sense for a client which wants to compile code to invoke complex subcomponents in order to compile.

A better approach would be to define a uniform interface which presents the compiler functionality – a Compiler class. The Compiler class hides “low-level” functionality from the client, so we can say that Compiler class is a facade.

The Facade design pattern hides the complexity of a system and provides an interface to the client through which the client can access the system.

Story

You want to organize a marriage reception with dinner for 100 people. So, in order to organize such event, you need to find and decorate a hall where the event will happen, then you need to organize the band, organize flowers, send invitations, and so on and so on.

If this is too much trouble for you, you could hire an event manager who will organize the event for you.

This is a typical example for Facade.

Image

alt text

Event Management in Pune, By WeMaxx1248 (Own work) [CC BY-SA 4.0], via Wikimedia Commons

UML

Structure

The UML diagram consist of Facade and subsystem classes.

Implementation

Compiler.java

package com.hundredwordsgof.facade;

import java.util.List;

/**
 * 
 * Compiler has subclasses like Tokenizer, Parser, Generator, etc. Client which
 * use a compiler do not deal with subclasses in order to compile. Compiler
 * class represents a facade. Facade hides low-level functionality from client.
 *
 */
public class Compiler {

  public static int compile(String input) throws Exception {

    Parser parser = new Parser();
    List<String> tokens = Tokenizer.tokenize(input);
    Node expression = parser.parse(tokens);
    int result = Generator.generate(expression);

    return result;
  }
}

Tokenizer.java

package com.hundredwordsgof.facade;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Tokenizer, tokenize input string into tokens.
 *
 */
public class Tokenizer {

  public static List<String> tokenize(String source) {

    ArrayList<String> tokens = new ArrayList<String>();

    StringTokenizer stringTokenizer = new StringTokenizer(source);
    while (stringTokenizer.hasMoreElements()) {
      tokens.add((String) stringTokenizer.nextElement());
    }
    return tokens;
  }
}

Generator.java

package com.hundredwordsgof.facade;

/**
 *
 * Generator, supposed to generate binary code, but in this case acts as a
 * calculator which adds two numbers.
 *
 */
public class Generator {

  public static int generate(Node expression) throws Exception {

    // TODO check expression
    if (expression instanceof ExpressionNode) {
      ExpressionNode expressionNode = (ExpressionNode) expression;

      OperandNode rightOperandNode = (OperandNode) expressionNode.getRight();
      OperandNode leftOperandNode = (OperandNode) expressionNode.getLeft();

      int result = rightOperandNode.getValue() + leftOperandNode.getValue();

      return result;

    } else {
      throw new Exception("Error in generator");
    }
  }
}

Node.java

package com.hundredwordsgof.facade;

/**
 * Node, represents Node in Abstract Syntax Tree.
 *
 */
public class Node {
	 
}

ExpressionNode.java

package com.hundredwordsgof.facade;

/**
 * ExpressionNode, represents ExpressionNode in Abstract Syntax Tree.
 *
 */
public class ExpressionNode extends Node {

  private char operator;
  private Node left;
  private Node right;

  public char getOperator() {
    return operator;
  }

  public void setOperator(char operator) {
    this.operator = operator;
  }

  public Node getLeft() {
    return left;
  }

  public void setLeft(Node left) {
    this.left = left;
  }

  public Node getRight() {
    return right;
  }

  public void setRight(Node right) {
    this.right = right;
  }
}

OperandNode.java

package com.hundredwordsgof.facade;

/**
 * OperandNode, represents OperandNode in Abstract Syntax Tree.
 *
 */
public class OperandNode extends Node {

  private int value;

  public int getValue() {
    return value;
  }

  public void setValue(int value) {
    this.value = value;
  }
}

Parser.java

package com.hundredwordsgof.facade;

import java.util.List;
import java.util.Stack;

/**
 * 
 * Parser parses simple expression which adds two numbers, for example: 1 + 2
 * Note: due to scope error handling is not implemented.
 *
 */
public class Parser {

  private Stack<String> expressionStack = new Stack<String>();
  private Stack<String> operandStack = new Stack<String>();

  public Node parse(List<String> tokens) {

    for (String token : tokens) {
      if (isTokenExpression(token)) {
        expressionStack.push(token);
      } else if (isTokenOperand(token)) {
        operandStack.push(token);
      }
    }

    ExpressionNode expressionNode = new ExpressionNode();

    // create Abstract Syntax Tree
    while (!expressionStack.empty()) {

      String expression = (String) expressionStack.pop();
      expressionNode.setOperator(expression.charAt(0));

      String rightOperand = (String) operandStack.pop();
      OperandNode rightOperandNode = new OperandNode();
      rightOperandNode.setValue(Integer.parseInt(rightOperand));

      String leftOperand = (String) operandStack.pop();
      OperandNode leftOperandNode = new OperandNode();
      leftOperandNode.setValue(Integer.parseInt(leftOperand));

      expressionNode.setRight(rightOperandNode);
      expressionNode.setLeft(leftOperandNode);

    }

    return expressionNode;
  }

  private boolean isTokenExpression(String token) {

    if (token.equals("+")) {
      return true;
    }
    return false;
  }

  // operand is supposed to be number
  private boolean isTokenOperand(String token) {
    for (char c : token.toCharArray()) {
      if (!Character.isDigit(c))
        return false;
    }
    return true;
  }
}

Usage

FacadeTest.java

package com.hundredwordsgof.facade;

import static org.junit.Assert.*;
import org.junit.Test;

/**
 * Test implementation of the Facade pattern.
 */
public class FacadeTest {

  @Test
  public void testFacade() throws Exception {

    // Compiler class is a facade, it can add two numbers with following
    // expression: 1 + 2
    // all other operations are not supported
    assertEquals(3, Compiler.compile("1 + 2"));

    try {
      // right operand is not a number
      Compiler.compile("1 + x");
      fail("Exception must be thrown");
    } catch (Exception e) {
    }

    try {
      // - expression is not supported
      Compiler.compile("1 - 1");
      fail("Exception must be thrown");
    } catch (Exception e) {
    }
  }
}