Bridge is a design pattern to isolate (not just encapsulate) volatile abstractions from the existing abstractions, making it simpler to integrate variable from multiple degrees. Example, say a threading scheduler has:
Hence, we got a native design pattern like this:
Using such design, although faster introduces a lot of duplication for the 2nd degree variable: operating system support. Hence, it is better to isolate the 2nd degree variable using what we called "Bridge" manner into this design:
This way, both the 1st degree variable (types of threading) and the 2nd degree variable (operating system supports) get completely isolated.
The diagram is shown as follows:
Here is an example in Go:
package mainimport ( "fmt")const ( PrinterA2 = uint(0) PrinterB2 = uint(1) PrinterCO = uint(2))/******************************************************************** * Printer (2nd Degree Variable) * ********************************************************************/// Printer is the platform printer interfaces for Print functionstype Printer interface { LocalPrint(data string)}// A2Printer is a type of printertype A2Printer struct {}func (p *A2Printer) LocalPrint(data string) { fmt.Printf("A2 Print - %v\n", data)}// B2Printer is a type of printertype B2Printer struct {}func (p *B2Printer) LocalPrint(data string) { fmt.Printf("B2 Print - %v\n", data)}// COPrinter is a type of printertype COPrinter struct {}func (p *COPrinter) LocalPrint(data string) { fmt.Printf("CO Print - %v\n", data)}/******************************************************************** * Pre-processing (1st Degree Variable) * ********************************************************************/// Terminal is the object client interacts with.type Terminal struct { printer Printer}// Init initializes the terminal with a specific printer.func (t *Terminal) Init(printerID uint) { switch printerID { case PrinterA2: t.printer = &A2Printer{} case PrinterB2: t.printer = &B2Printer{} case PrinterCO: t.printer = &COPrinter{} }}// Print asks user to print something.func (t *Terminal) Print(data string) { if t.printer == nil { return } d := fmt.Sprintf("term: %v", data) t.printer.LocalPrint(d)}/******************************************************************** * Client * ********************************************************************/func main() { t := &Terminal{} // first print using A2 configurations t.Init(PrinterA2) t.Print("Hello World") // first print using B2 configurations t.Init(PrinterB2) t.Print("Bonjour Le Monde") // third print using CO configurations t.Init(PrinterCO) t.Print("こんにちは世界")}// Output:// A2 Print - term: Hello World// B2 Print - term: Bonjour Le Monde// CO Print - term: こんにちは世界Notice that there are 3 layers of interface:
Printer interface. It selects the correct printer based on the user input and use the Printer accordingly.Printer interface. It facilitates its own evolution and growth (add more printer) independently from the Terminal structure. Hence, they are isolated from each other.