Facade
Objective
Provide a unified interface for a set of interfaces of a subsystem, facilitating the use of that subsystem.
Function
Facilitate the addition of functionality to a class dynamically.
Structure
Facade provides convenient access to a particular part of the functionality of the subsystem. You know where to address the customer's request and how to operate all moving parts.
The structure that meets this pattern is shown in Figure 1
Figure 1: UML Diagram Facade Pattern
Applications
The use of the Facade pattern is recommended when:
- There are too many dependencies between clients and the implementation of classes, facilitating portability.
- The aim is to define an entry point for each subsystem, to simplify the interaction between subsystems through their respective implementations of the Facade pattern.
- Avoid the increasing complexity of the subsystems due to their evolution process, by implementing a simple interface.
Design Patterns Collaborators
- The Facade and Abstract Factory patterns can work together to provide an interface for creating subsystem objects independently.
- The Facade and Mediator patterns are similar in that they abstract the of existing classes, considering that the purpose of Mediator is to summarize the communication between fellow objects.
Scope of action
Applied at the object level.
Problem
The implementation of a solution that simplifies the use of a complex subsystem is carried out by means of an encapsulation process, which in many occasions turns out not to be the best way.
Solution
The Facade pattern introduces an object that provides a single interface simplified by minimizing communication and dependencies between subsystems. The object performs a complex task since it must translate the interface of each subsystem to a common language to allow interaction between them.
Diagram or Implementation
Figure 2: UML Diagram Facade Pattern
Figure 2 explains the behaviour of the Facade pattern using a sequence diagram.
- The client class invokes a Facade class transaction.
- The Facade class communicates with the SubsystemA component to perform a operation.
- The Facade class communicates with the SubsystemB component to perform a operation.
- The Facade class communicates with the SubsystemC component to perform a operation.
- The Facade class responds to the client class with the result of the operation.
Implementations of the facade pattern:
"""
Provide a unified interface to a set of interfaces in a subsystem.
Facade defines a higher-level interface that makes the subsystem easier
to use.
"""
class Facade:
"""
Know which subsystem classes are responsible for a request.
Delegate client requests to appropriate subsystem objects.
"""
def __init__(self):
self._subsystem_1 = Subsystem1()
self._subsystem_2 = Subsystem2()
def operation(self):
self._subsystem_1.operation1()
self._subsystem_1.operation2()
self._subsystem_2.operation1()
self._subsystem_2.operation2()
class Subsystem1:
"""
Implement subsystem functionality.
Handle work assigned by the Facade object.
Have no knowledge of the facade; that is, they keep no references to
it.
"""
def operation1(self):
pass
def operation2(self):
pass
class Subsystem2:
"""
Implement subsystem functionality.
Handle work assigned by the Facade object.
Have no knowledge of the facade; that is, they keep no references to
it.
"""
def operation1(self):
pass
def operation2(self):
pass
def main():
facade = Facade()
facade.operation()
if __name__ == "__main__":
main()
class Book {
private $author;
private $title;
function __construct($title_in, $author_in) {
$this->author = $author_in;
$this->title = $title_in;
}
function getAuthor() {
return $this->author;
}
function getTitle() {
return $this->title;
}
function getAuthorAndTitle() {
return $this->getTitle().' by '.$this->getAuthor();
}
}
class CaseReverseFacade {
public static function reverseStringCase($stringIn) {
$arrayFromString = ArrayStringFunctions::stringToArray($stringIn);
$reversedCaseArray = ArrayCaseReverse::reverseCase($arrayFromString);
$reversedCaseString = ArrayStringFunctions::arrayToString($reversedCaseArray);
return $reversedCaseString;
}
}
class ArrayCaseReverse {
private static $uppercase_array =
array('A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z');
private static $lowercase_array =
array('a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x',
'y', 'z');
public static function reverseCase($arrayIn) {
$array_out = array();
for ($x = 0; $x < count($arrayIn); $x++) {
if (in_array($arrayIn[$x], self::$uppercase_array)) {
$key = array_search($arrayIn[$x], self::$uppercase_array);
$array_out[$x] = self::$lowercase_array[$key];
} elseif (in_array($arrayIn[$x], self::$lowercase_array)) {
$key = array_search($arrayIn[$x], self::$lowercase_array);
$array_out[$x] = self::$uppercase_array[$key];
} else {
$array_out[$x] = $arrayIn[$x];
}
}
return $array_out;
}
}
class ArrayStringFunctions {
public static function arrayToString($arrayIn) {
$string_out = NULL;
foreach ($arrayIn as $oneChar) {
$string_out .= $oneChar;
}
return $string_out;
}
public static function stringToArray($stringIn) {
return str_split($stringIn);
}
}
witeln('BEGIN TESTING FACADE PATTERN');
witeln('');
$book = new Book('Design Patterns', 'Gamma, Helm, Johnson, and Vlissides');
witeln('Original book title: '.$book->getTitle());
witeln('');
$bookTitleReversed = CaseReverseFacade::reverseStringCase($book->getTitle());
writeln('Reversed book title: '.$bookTitleReversed);
witeln('');
writeln('END TESTING FACADE PATTERN');
function writeln($line_in) {
echo $line_in."<br/>";
}
// 1. Subsystem
class PointCartesian {
private double x, y;
public PointCartesian(double x, double y ) {
this.x = x;
this.y = y;
}
public void move( int x, int y ) {
this.x += x;
this.y += y;
}
public String toString() {
return "(" + x + "," + y + ")";
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
// 1. Subsystem
class PointPolar {
private double radius, angle;
public PointPolar(double radius, double angle) {
this.radius = radius;
this.angle = angle;
}
public void rotate(int angle) {
this.angle += angle % 360;
}
public String toString() {
return "[" + radius + "@" + angle + "]";
}
}
// 1. Desired interface: move(), rotate()
class Point {
// 2. Design a "wrapper" class
private PointCartesian pointCartesian;
public Point(double x, double y) {
pointCartesian = new PointCartesian(x, y);
}
public String toString() {
return pointCartesian.toString();
}
// 4. Wrapper maps
public void move(int x, int y) {
pointCartesian.move(x, y);
}
public void rotate(int angle, Point o) {
double x = pointCartesian.getX() - o.pointCartesian.getX();
double y = pointCartesian.getY() - o.pointCartesian.getY();
PointPolar pointPolar = new PointPolar(Math.sqrt(x * x + y * y),Math.atan2(y, x) * 180 / Math.PI);
// 4. Wrapper maps
pointPolar.rotate(angle);
System.out.println(" PointPolar is " + pointPolar);
String str = pointPolar.toString();
int i = str.indexOf('@');
double r = Double.parseDouble(str.substring(1, i));
double a = Double.parseDouble(str.substring(i + 1, str.length() - 1));
pointCartesian = new PointCartesian(r*Math.cos(a*Math.PI/180) + o.pointCartesian.getX(),
r*Math.sin(a * Math.PI / 180) + o.pointCartesian.getY());
}
}
class Line {
private Point o, e;
public Line(Point ori, Point end) {
o = ori;
e = end;
}
public void move(int x, int y) {
o.move(x, y);
e.move(x, y);
}
public void rotate(int angle) {
e.rotate(angle, o);
}
public String toString() {
return "origin is " + o + ", end is " + e;
}
}
public class FacadeDemo {
public static void main(String[] args) {
// 3. Client uses the Facade
Line lineA = new Line(new Point(2, 4), new Point(5, 7));
lineA.move(-2, -4);
System.out.println( "after move: " + lineA );
lineA.rotate(45);
System.out.println( "after rotate: " + lineA );
Line lineB = new Line( new Point(2, 1), new Point(2.866, 1.5));
lineB.rotate(30);
System.out.println("30 degrees to 60 degrees: " + lineB);
}
}