“Hardening of the software arteries” has occurred by using subclassing of an abstract base class to provide alternative implementations. This locks in compile-time binding between interface and implementation. The abstraction and implementation cannot be independently extended or composed.
Consider the domain of “thread scheduling”.
There are two types of thread schedulers, and two types of operating systems or “platforms”. Given this approach to specialization, we have to define a class for each permutation of these two dimensions. If we add a new platform (say … Java’s Virtual Machine), what would our hierarchy look like?
What if we had three kinds of thread schedulers, and four kinds of platforms? What if we had five kinds of thread schedulers, and ten kinds of platforms? The number of classes we would have to define is the product of the number of scheduling schemes and the number of platforms.
The Bridge design pattern proposes refactoring this exponentially explosive inheritance hierarchy into two orthogonal hierarchies – one for platform-independent abstractions, and the other for platform-dependent implementations.
Decompose the component’s interface and implementation into orthogonal class hierarchies. The interface class contains a pointer to the abstract implementation class. This pointer is initialized with an instance of a concrete implementation class, but all subsequent interaction from the interface class to the implementation class is limited to the abstraction maintained in the implementation base class. The client interacts with the interface class, and it in turn “delegates” all requests to the implementation class.
The interface object is the “handle” known and used by the client; while the implementation object, or “body”, is safely encapsulated to ensure that it may continue to evolve, or be entirely replaced (or shared at run-time.
Bridge is a synonym for the “handle/body” idiom. This is a design mechanism that encapsulates an implementation class inside of an interface class. The former is the body, and the latter is the handle. The handle is viewed by the user as the actual class, but the work is done in the body. “The handle/body class idiom may be used to decompose a complex abstraction into smaller, more manageable classes. The idiom may reflect the sharing of a single resource by multiple classes that control access to it (e.g. reference counting).”
The Client doesn’t want to deal with platform-dependent details. The Bridge pattern encapsulates this complexity behind an abstraction “wrapper”.
Bridge emphasizes identifying and decoupling “interface” abstraction from “implementation” abstraction.
The Bridge pattern decouples an abstraction from its implementation, so that the two can vary independently. A household switch controlling lights, ceiling fans, etc. is an example of the Bridge. The purpose of the switch is to turn a device on or off. The actual switch can be implemented as a pull chain, simple two position switch, or a variety of dimmer switches.
1: unit Pattern;
2:3: interface
4:5: uses
6: SysUtils;7:8: type
9: IBridge = interface
10: ['{FB1F4FB1-8AD8-48FD-AEA5-1E33B4628879}']11: function OperationImp: String;
12: end;
13:14: TAbstraction = class15: private16: FBridge: IBridge;17: public18: constructor Create(implement: IBridge);
19: function Operation: string;20: end;
21:22: TImplementationA = class(TinterfacedObject, IBridge)23: public24: function OperationImp: string;25: end;
26:27: TImplementationB = class(TInterfacedObject, IBridge)28: public29: function OperationImp: string;30: end;
31:32: implementation
33:34: { TAbstraction }35:36: constructor TAbstraction.Create(implement: IBridge);
37: begin
38: // inherited;
39: FBridge := implement;40: end;
41:42: function TAbstraction.Operation: string;43: begin
44: WriteLn('Abstraction' + ' <<>> ' + FBridge.OperationImp);45: end;
46:47: { TImplemetationA }48:49: function TImplementationA.OperationImp: string;50: begin
51: Result := 'Implementation A';52: end;
53:54: { TImplementationB }55:56: function TImplementationB.OperationImp: string;57: begin
58: Result := 'Implementation B';59: end;
60:61: end.
--------------------------------------------------------------------------------------------------------------------------
1: program Structural.Bridge.Pattern;
2:3: {$APPTYPE CONSOLE}4:5: uses
6: SysUtils,7: Pattern in 'Pattern.pas';
8:9: var
10: abstraction: TAbstraction;11:12: begin
13: // ReportMemoryLeaksOnShutdown := DebugHook 0;
14: try15: try16: abstraction := TAbstraction.Create(TImplementationA.Create);17: WriteLn(abstraction.Operation);18:19: abstraction.Free;20: abstraction := TAbstraction.Create(TImplementationB.Create);21: WriteLn(abstraction.Operation);22:23: ReadLn;24: finally25: FreeAndNil(abstraction);26: end;
27: except28: on E:Exception do
29: Writeln(E.Classname, ': ', E.Message);30: end;
31: end.