Card 03/ 08
Bridge Template in Python, Java and TypeScript
Two abstractions and two implementors, combined at construction time.
The Bridge template is the pattern in its pure form, with the participant names from the class diagram and no domain. Each operation reports which abstraction ran and which implementor it called, and the client builds every pair, so the output shows four combinations from four classes. The steps add each participant in Python, and the tabs give the complete program in three languages, each with its output.
Building the template one participant at a time
Implementor
The interface for the low-level side.
class Implementor(ABC):
@abstractmethod
def operation_impl(self) -> str: ...Concrete implementors
One class per implementation.
class ConcreteImplementorA(Implementor):
def operation_impl(self) -> str:
return "ImplementorA"Abstraction
Holds an implementor, which is the bridge, and calls it.
class Abstraction:
def __init__(self, implementor: Implementor) -> None:
self._implementor = implementorRefined abstraction
A variant of the abstraction that uses the same reference.
class RefinedAbstraction(Abstraction):
def operation(self) -> str:
return f"RefinedAbstraction with {self._implementor.operation_impl()}"The complete template
from abc import ABC, abstractmethod
class Implementor(ABC):
@abstractmethod
def operation_impl(self) -> str: ...
class ConcreteImplementorA(Implementor):
def operation_impl(self) -> str:
return "ImplementorA"
class ConcreteImplementorB(Implementor):
def operation_impl(self) -> str:
return "ImplementorB"
class Abstraction:
def __init__(self, implementor: Implementor) -> None:
self._implementor = implementor
def operation(self) -> str:
return f"Abstraction with {self._implementor.operation_impl()}"
class RefinedAbstraction(Abstraction):
def operation(self) -> str:
return f"RefinedAbstraction with {self._implementor.operation_impl()}"
for abstraction in (Abstraction, RefinedAbstraction):
for implementor in (ConcreteImplementorA(), ConcreteImplementorB()):
print(abstraction(implementor).operation())Abstraction with ImplementorA
Abstraction with ImplementorB
RefinedAbstraction with ImplementorA
RefinedAbstraction with ImplementorBimport java.util.List;
import java.util.function.Function;
public class BrTemplate {
public static void main(String[] args) {
List<Function<Implementor, Abstraction>> abstractions =
List.of(Abstraction::new, RefinedAbstraction::new);
List<Implementor> implementors =
List.of(new ConcreteImplementorA(), new ConcreteImplementorB());
for (Function<Implementor, Abstraction> create : abstractions) {
for (Implementor implementor : implementors) {
System.out.println(create.apply(implementor).operation());
}
}
}
}
interface Implementor {
String operationImpl();
}
class ConcreteImplementorA implements Implementor {
public String operationImpl() {
return "ImplementorA";
}
}
class ConcreteImplementorB implements Implementor {
public String operationImpl() {
return "ImplementorB";
}
}
class Abstraction {
protected final Implementor implementor;
Abstraction(Implementor implementor) {
this.implementor = implementor;
}
String operation() {
return "Abstraction with " + implementor.operationImpl();
}
}
class RefinedAbstraction extends Abstraction {
RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
String operation() {
return "RefinedAbstraction with " + implementor.operationImpl();
}
}Abstraction with ImplementorA
Abstraction with ImplementorB
RefinedAbstraction with ImplementorA
RefinedAbstraction with ImplementorBinterface Implementor {
operationImpl(): string;
}
class ConcreteImplementorA implements Implementor {
operationImpl(): string {
return "ImplementorA";
}
}
class ConcreteImplementorB implements Implementor {
operationImpl(): string {
return "ImplementorB";
}
}
class Abstraction {
constructor(protected readonly implementor: Implementor) {}
operation(): string {
return `Abstraction with ${this.implementor.operationImpl()}`;
}
}
class RefinedAbstraction extends Abstraction {
operation(): string {
return `RefinedAbstraction with ${this.implementor.operationImpl()}`;
}
}
for (const create of [Abstraction, RefinedAbstraction]) {
for (const implementor of [new ConcreteImplementorA(), new ConcreteImplementorB()]) {
console.log(new create(implementor).operation());
}
}Abstraction with ImplementorA
Abstraction with ImplementorB
RefinedAbstraction with ImplementorA
RefinedAbstraction with ImplementorBFour lines of output come from two abstractions and two implementors, and no class names a pair. A third implementor would make six lines from five classes. The client chooses each pair when it constructs the abstraction, which is also where a real program would pick an implementation from configuration.
← → move through sections and cards