Bridge
Objective
Separate an abstraction from its implementation, so that both may vary independently.
Function
Decouple an abstraction from its implementation.
Structure
The client class does not want to deal with platform-dependent details. The Bridge pattern encapsulates this complexity behind a 'wrap'. of abstraction.Bridge emphasizes the identification and decoupling of "interface" abstraction from "implementation" abstraction.
The structure that meets this pattern is shown in Figure 1
Figure 1: UML Diagram Bridge Pattern
Applications
- You want to share an implementation among multiple objects, and that this fact is transparent to the client.
- A permanent link between an abstraction and its implementation needs to be removed, for example, when an implementation needs to be linked or changed at run time.
- The need to implement various representations of an abstraction can generate a proliferation of classes.
- Abstraction and implementation must be extensible through aggregation of subclasses. In this case the bridge design pattern allows the combination of different abstractions and implementations and extend them independently.
- Changes in the implementation of an abstraction should not impact the client classes, their code should not be recompiled.
Design Patterns Collaborators
- The Abstract Factory pattern can create and set a certain bridge pattern.
Scope of action
Applied at the object level.
Problem
To make use of different representations of an abstract class, it is required to implement an inheritance since an abstract class defines the interface for such abstraction and the concrete, or inherited, classes implement the different ways, however this implementation is limiting, considering that permanently linked to abstraction, which makes it difficult to modify, extend, and reusing abstractions and implementations independently.
Solution
The bridge design pattern allows a bridge to connect a abstraction of its different implementations, which creates a hierarchy for the abstractions and another for the implementations, allowing to manage both class hierarchies independently.
Diagram or Implementation
Figure 2: UML Diagram Bridge Pattern
Figure 2 explains the behaviour of the bridge pattern by means of a sequence diagram.
- Client class executes an AbstractionImplement operation.
- The class AbstractionImplement replicates the request to ConcreteImplementor, in this step the class AbstractionImplement could perform a conversion of the parameters to execute the ConcreteImplementor.
- ConcreteImplementor returns the results to the AbstractionImplement class.
- Finally the AbstractionImplement class converts the results of the ConcreteImplementor to be returned to the client.
Study Cases
Drawing Editor System
Figure 3: UML Diagram Drawing Editor System
Figure 4: UML Diagram Drawing Editor System
Tourist Reservation System
Figure 5: UML Diagram Tourist Reservation System
Figure 6: UML Diagram Tourist Reservation System
Implementations of the bridge pattern:
"""
Decouple an abstraction from its implementation so that the two can vary
independently.
"""
import abc
class Abstraction:
"""
Define the abstraction's interface.
Maintain a reference to an object of type Implementor.
"""
def __init__(self, imp):
self._imp = imp
def operation(self):
self._imp.operation_imp()
class Implementor(metaclass=abc.ABCMeta):
"""
Define the interface for implementation classes. This interface
doesn't have to correspond exactly to Abstraction's interface; in
fact the two interfaces can be quite different. Typically the
Implementor interface provides only primitive operations, and
Abstraction defines higher-level operations based on these
primitives.
"""
@abc.abstractmethod
def operation_imp(self):
pass
class ConcreteImplementorA(Implementor):
"""
Implement the Implementor interface and define its concrete
implementation.
"""
def operation_imp(self):
pass
class ConcreteImplementorB(Implementor):
"""
Implement the Implementor interface and define its concrete
implementation.
"""
def operation_imp(self):
pass
def main():
concrete_implementor_a = ConcreteImplementorA()
abstraction = Abstraction(concrete_implementor_a)
abstraction.operation()
if __name__ == "__main__":
main()
abstract class BridgeBook {
private $bbAuthor;
private $bbTitle;
private $bbImp;
function __construct($author_in, $title_in, $choice_in) {
$this->bbAuthor = $author_in;
$this->bbTitle = $title_in;
if ('STARS' == $choice_in) {
$this->bbImp = new BridgeBookStarsImp();
} else {
$this->bbImp = new BridgeBookCapsImp();
}
}
function showAuthor() {
return $this->bbImp->showAuthor($this->bbAuthor);
}
function showTitle() {
return $this->bbImp->showTitle($this->bbTitle);
}
}
class BridgeBookAuthorTitle extends BridgeBook {
function showAuthorTitle() {
return $this->showAuthor() . "'s " . $this->showTitle();
}
}
class BridgeBookTitleAuthor extends BridgeBook {
function showTitleAuthor() {
return $this->showTitle() . ' by ' . $this->showAuthor();
}
}
abstract class BridgeBookImp {
abstract function showAuthor($author);
abstract function showTitle($title);
}
class BridgeBookCapsImp extends BridgeBookImp {
function showAuthor($author_in) {
return strtoupper($author_in);
}
function showTitle($title_in) {
return strtoupper($title_in);
}
}
class BridgeBookStarsImp extends BridgeBookImp {
function showAuthor($author_in) {
return Str_replace(" ","*",$author_in);
}
function showTitle($title_in) {
return Str_replace(" ","*",$title_in);
}
}
writeln('BEGIN TESTING BRIDGE PATTERN');
writeln('');
writeln('test 1 - author title with caps');
$book = new BridgeBookAuthorTitle('Larry Truett','PHP for Cats','CAPS');
writeln($book->showAuthorTitle());
writeln('');
writeln('test 2 - author title with stars');
$book = new BridgeBookAuthorTitle('Larry Truett','PHP for Cats','STARS');
writeln($book->showAuthorTitle());
writeln('');
writeln('test 3 - title author with caps');
$book = new BridgeBookTitleAuthor('Larry Truett','PHP for Cats','CAPS');
writeln($book->showTitleAuthor());
writeln('');
writeln('test 4 - title author with stars');
$book = new BridgeBookTitleAuthor('Larry Truett','PHP for Cats','STARS');
writeln($book->showTitleAuthor());
writeln('');
writeln('END TESTING BRIDGE PATTERN');
function writeln($line_in) {
echo $line_in."
";
}
class Node {
public int value;
public Node prev, next;
public Node(int i) {
value = i;
}
}
class Stack {
private StackImpl impl;
public Stack( String s ) {
if (s.equals("array")) {
impl = new StackArray();
} else if (s.equals("list")) {
impl = new StackList();
} else {
System.out.println("Stack: unknown parameter");
}
}
public Stack() {
this("array");
}
public void push(int in) {
impl.push( in );
}
public int pop() {
return impl.pop();
}
public int top() {
return impl.top();
}
public boolean isEmpty() {
return impl.isEmpty();
}
public boolean isFull() {
return impl.isFull();
}
}
class StackHanoi extends Stack {
private int totalRejected = 0;
public StackHanoi() {
super("array");
}
public StackHanoi(String s) {
super(s);
}
public int reportRejected() {
return totalRejected;
}
public void push(int in) {
if (!isEmpty() && in > top()) {
totalRejected++;
}
else {
super.push(in);
}
}
}
class StackFIFO extends Stack {
private StackImpl stackImpl = new StackList();
public StackFIFO() {
super("array");
}
public StackFIFO(String s) {
super(s);
}
public int pop() {
while (!isEmpty()) {
stackImpl.push(super.pop());
}
int ret = stackImpl.pop();
while (!stackImpl.isEmpty()) {
push(stackImpl.pop());
}
return ret;
}
}
interface StackImpl {
void push(int i);
int pop();
int top();
boolean isEmpty();
boolean isFull();
}
class StackArray implements StackImpl {
private int[] items;
private int total = -1;
public StackArray() {
this.items = new int[12];
}
public StackArray(int cells) {
this.items = new int[cells];
}
public void push(int i) {
if (!isFull()) {
items[++total] = i;
}
}
public boolean isEmpty() {
return total == -1;
}
public boolean isFull() {
return total == items.length - 1;
}
public int top() {
if (isEmpty()) {
return -1;
}
return items[total];
}
public int pop() {
if (isEmpty()) {
return -1;
}
return items[total--];
}
}
class StackList implements StackImpl {
private Node last;
public void push(int i) {
if (last == null) {
last = new Node(i);
} else {
last.next = new Node(i);
last.next.prev = last;
last = last.next;
}
}
public boolean isEmpty() {
return last == null;
}
public boolean isFull() {
return false;
}
public int top() {
if (isEmpty()) {
return -1;
}
return last.value;
}
public int pop() {
if (isEmpty()) {
return -1;
}
int ret = last.value;
last = last.prev;
return ret;
}
}
public class BridgeDisk {
public static void main(String[] args) {
Stack[] stacks = {new Stack("array"), new Stack("list"),
new StackFIFO(), new StackHanoi()};
for (int i=1, num; i < 15; i++) {
for (int j=0; j < 3; j++) {
stacks[j].push( i );
}
}
Random rn = new Random();
for (int i=1, num; i < 15; i++) {
stacks[3].push(rn.nextInt(20));
}
for (int i=0, num; i < stacks.length; i++) {
while (!stacks[i].isEmpty()) {
System.out.print(stacks[i].pop() + " ");
}
System.out.println();
}
System.out.println("total rejected is " + ((StackHanoi)stacks[3]).reportRejected());
}
}