Facade
A Facade pattern hides the complexities of the system and provides an interface to the client from where the client can access the system. Dividing a system into subsystems helps reduce complexity. We need to minimize the communication and dependencies between subsystems. For this, we introduce a facade object that provides a single, simplified interface to the more general facilities of a subsystem.
Examples
The SCP
command is a shortcut for SSH commands. A remote file copy could be done writing several commands with an SSH connection but it can be done in one command with SCP
. So the SCP
command is a facade for the SSH commands. Although it may not be coded in the object programming paradigm, it is a good illustration of the design pattern.
Cost
This pattern is very easy and has not additional cost.
Creation
This pattern is very easy to create.
Maintenance
This pattern is very easy to maintain.
Removal
This pattern is very easy to remove too.
Advises
- Do not use this pattern to mask only three or four method calls.
Implementations
This is an abstract example of how a client ("you") interacts with a facade (the "computer") to a complex system (internal computer parts, like CPU and HardDrive).
/* Complex parts */
class CPU {
public void freeze() { ... }
public void jump(long position) { ... }
public void execute() { ... }
}
class Memory {
public void load(long position, byte[] data) { ... }
}
class HardDrive {
public byte[] read(long lba, int size) { ... }
}
/* Facade */
class Computer {
private CPU processor;
private Memory ram;
private HardDrive hd;
public Computer() {
this.processor = new CPU();
this.ram = new Memory();
this.hd = new HardDrive();
}
public void start() {
processor.freeze();
ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
processor.jump(BOOT_ADDRESS);
processor.execute();
}
}
/* Client */
class You {
public static void main(String[] args) {
Computer facade = new Computer();
facade.start();
}
}
using System;
namespace Facade
{
public class CPU
{
public void Freeze() { }
public void Jump(long addr) { }
public void Execute() { }
}
public class Memory
{
public void Load(long position, byte[] data) { }
}
public class HardDrive
{
public byte[] Read(long lba, int size) { return null; }
}
public class Computer
{
var cpu = new CPU();
var memory = new Memory();
var hardDrive = new HardDrive();
public void StartComputer()
{
cpu.Freeze();
memory.Load(0x22, hardDrive.Read(0x66, 0x99));
cpu.Jump(0x44);
cpu.Execute();
}
}
public class SomeClass
{
public static void Main(string[] args)
{
var facade = new Computer();
facade.StartComputer();
}
}
}
# Complex parts
class CPU
def freeze; puts 'CPU: freeze'; end
def jump(position); puts "CPU: jump to #{position}"; end
def execute; puts 'CPU: execute'; end
end
class Memory
def load(position, data)
puts "Memory: load #{data} at #{position}"
end
end
class HardDrive
def read(lba, size)
puts "HardDrive: read sector #{lba} (#{size} bytes)"
return 'hdd data'
end
end
# Facade
class Computer
BOOT_ADDRESS = 0
BOOT_SECTOR = 0
SECTOR_SIZE = 512
def initialize
@cpu = CPU.new
@memory = Memory.new
@hard_drive = HardDrive.new
end
def start_computer
@cpu.freeze
@memory.load(BOOT_ADDRESS, @hard_drive.read(BOOT_SECTOR, SECTOR_SIZE))
@cpu.jump(BOOT_ADDRESS)
@cpu.execute
end
end
# Client
facade = Computer.new
facade.start_computer
# Complex parts
class CPU:
def freeze(self): pass
def jump(self, position): pass
def execute(self): pass
class Memory:
def load(self, position, data): pass
class HardDrive:
def read(self, lba, size): pass
# Facade
class Computer:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.hard_drive = HardDrive()
def start_computer(self):
self.cpu.freeze()
self.memory.load(0, self.hard_drive.read(0, 1024))
self.cpu.jump(10)
self.cpu.execute()
# Client
if __name__ == '__main__':
facade = Computer()
facade.start_computer()
/* Complex parts */
class CPU
{
public function freeze() { /* ... */ }
public function jump( $position ) { /* ... */ }
public function execute() { /* ... */ }
}
class Memory
{
public function load( $position, $data ) { /* ... */ }
}
class HardDrive
{
public function read( $lba, $size ) { /* ... */ }
}
/* Facade */
class Computer
{
protected $cpu = null;
protected $memory = null;
protected $hardDrive = null;
public function __construct()
{
$this->cpu = new CPU();
$this->memory = new Memory();
$this->hardDrive = new HardDrive();
}
public function startComputer()
{
$this->cpu->freeze();
$this->memory->load( BOOT_ADDRESS, $this->hardDrive->read( BOOT_SECTOR, SECTOR_SIZE ) );
$this->cpu->jump( BOOT_ADDRESS );
$this->cpu->execute();
}
}
/* Client */
$facade = new Computer();
$facade->startComputer();
/* Complex parts */
var CPU = function () {};
CPU.prototype = {
freeze: function () {
console.log('CPU: freeze');
},
jump: function (position) {
console.log('CPU: jump to ' + position);
},
execute: function () {
console.log('CPU: execute');
}
};
var Memory = function () {};
Memory.prototype = {
load: function (position, data) {
console.log('Memory: load "' + data + '" at ' + position);
}
};
var HardDrive = function () {};
HardDrive.prototype = {
read: function (lba, size) {
console.log('HardDrive: read sector ' + lba + '(' + size + ' bytes)');
return 'hdd data';
}
};
/* Facade */
var Computer = function () {
var cpu, memory, hardDrive;
cpu = new CPU();
memory = new Memory();
hardDrive = new HardDrive();
var constant = function (name) {
var constants = {
BOOT_ADDRESS: 0,
BOOT_SECTOR: 0,
SECTOR_SIZE: 512
};
return constants[name];
};
this.startComputer = function () {
cpu.freeze();
memory.load(constant('BOOT_ADDRESS'), hardDrive.read(constant('BOOT_SECTOR'), constant('SECTOR_SIZE')));
cpu.jump(constant('BOOT_ADDRESS'));
cpu.execute();
}
};
/* Client */
var facade = new Computer();
facade.startComputer();
/* Complex Parts */
/* CPU.as */
package
{
public class CPU
{
public function freeze():void
{
trace("CPU::freeze");
}
public function jump(addr:Number):void
{
trace("CPU::jump to", String(addr));
}
public function execute():void
{
trace("CPU::execute");
}
}
}
/* Memory.as */
package
{
import flash.utils.ByteArray;
public class Memory
{
public function load(position:Number, data:ByteArray):void
{
trace("Memory::load position:", position, "data:", data);
}
}
}
/* HardDrive.as */
package
{
import flash.utils.ByteArray;
public class HardDrive
{
public function read(lba:Number, size:int):ByteArray
{
trace("HardDrive::read returning null");
return null;
}
}
}
/* The Facade */
/* Computer.as */
package
{
public class Computer
{
public static const BOOT_ADDRESS:Number = 0x22;
public static const BOOT_SECTOR:Number = 0x66;
public static const SECTOR_SIZE:int = 0x200;
private var _cpu:CPU;
private var _memory:Memory;
private var _hardDrive:HardDrive;
public function Computer()
{
_cpu = new CPU();
_memory = new Memory();
_hardDrive = new HardDrive();
}
public function startComputer():void
{
_cpu.freeze();
_memory.load(BOOT_ADDRESS, _hardDrive.read(BOOT_SECTOR, SECTOR_SIZE));
_cpu.jump(BOOT_ADDRESS);
_cpu.execute();
}
}
}
/* Client.as : This is the application's Document class */
package
{
import flash.display.MovieClip;
public class Client extends MovieClip
{
private var _computer:Computer;
public function Client()
{
_computer = new Computer();
_computer.startComputer();
}
}
}
/* Complex parts */
package intel {
class CPU {
def freeze() = ???
def jump(position: Long) = ???
def execute() = ???
}
}
package ram.plain {
class Memory {
def load(position: Long, data: Array[Byte]) = ???
}
}
package hdd {
class HardDrive {
def read(lba: Long, size: Int): Array[Byte] = ???
}
}
/* Facade */
//imports for the facade
import common.patterns.intel.CPU
import common.patterns.ram.plain.Memory
import common.patterns.hdd.HardDrive
package pk {
class ComputerFacade(conf: String) {
val processor: CPU = new CPU
val ram: Memory = new Memory
val hd: HardDrive = new HardDrive
val BOOT_ADDRESS: Long = ???
val BOOT_SECTOR: Long = ???
val SECTOR_SIZE: Int = ???
def start() = {
processor.freeze()
ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE))
processor.jump(BOOT_ADDRESS)
processor.execute()
}
}
}
//imports for your package
import common.patterns.pk.ComputerFacade
/* Client */
object You {
def main(args: Array[String]) {
new ComputerFacade("conf").start()
}
}
program Facade;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
(* complex parts - > Subsystem *)
TCPU = class
procedure Freeze;
procedure Jump(position: Integer);
procedure Execute;
end;
TMemory = class
procedure Load(position: Integer; data: string);
end;
THardDrive = class
function Read(lba, size: Integer): string;
end;
(* Facade *)
TComputer = class
fCPU: TCPU;
fMemory: TMemory;
fHardDrive: THardDrive;
const
BOOT_ADDRESS: Integer = 0;
BOOT_SECTOR: Integer = 0;
SECTOR_SIZE: Integer = 512;
public
procedure Start_Computer;
constructor Create;
end;
{ TCPU }
procedure TCPU.Execute;
begin
WriteLn('CPU: execute');
end;
procedure TCPU.Freeze;
begin
WriteLn('CPU: freese');
end;
procedure TCPU.Jump(position: Integer);
begin
WriteLn('CPU: jump to ' + IntToStr(position));
end;
{ TMemory }
procedure TMemory.Load(position: Integer; data: string);
begin
WriteLn('Memory: load "' + data + '" at ' + IntToStr(position));
end;
{ THardDrive }
function THardDrive.Read(lba, size: Integer): string;
begin
WriteLn('HardDrive: read sector ' + IntToStr(lba) + ' (' + IntToStr(size) +
' bytes)');
Result := 'hdd data';
end;
{ TComputer }
constructor TComputer.Create;
begin
fCPU := TCPU.Create;
fMemory := TMemory.Create;
fHardDrive := THardDrive.Create;
end;
procedure TComputer.Start_Computer;
begin
fCPU.Freeze;
fMemory.Load(BOOT_ADDRESS, fHardDrive.Read(BOOT_SECTOR, SECTOR_SIZE));
fCPU.Jump(BOOT_ADDRESS);
fCPU.Execute;
end;
var
facad: TComputer;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
facad := TComputer.Create;
facad.Start_Computer;
WriteLn(#13#10 + 'Press any key to continue...');
ReadLn;
facad.Free;
except
on E: Exception do
WriteLn(E.ClassName, ': ', E.Message);
end;
end.