桥接模式
简介
桥接模式(Bridge Pattern)是将实现类封装在接口或抽象类内部的设计模式。桥接模式将抽象部分与实现部分分离,使它们可以独立变化。它是用组合关系替代继承关系实现的,可以降低抽象部分和实现部分这两个可变维度的耦合度。
角色组成:
- Abstraction (抽象类): 定义抽象类的接口,并维护一个指向实现者(Implementor)对象的引用。它通常包含一些基本的业务逻辑,并将具体实现的操作委托给 Implementor 对象。
- RefinedAbstraction (具体抽象类/扩展抽象类): 继承自 Abstraction,扩展由 Abstraction 定义的接口,实现更具体的业务逻辑。
- Implementor (实现者接口): 定义实现类的接口,该接口不一定要与 Abstraction 的接口完全一致。通常它 只提供基本操作,而 Abstraction 则基于这些基本操作定义更高层次的操作。
- ConcreteImplementor (具体实现者类): 实现 Implementor 接口,给出具体的实现。
常见使用场景
- 当你不希望抽象和它的实现部分之间有一个固定的绑定关系时。 例如,你希望在程序运行时动态地选择或切换实现。
- 当抽象及其实现都可通过子类化独立进行扩展时。 桥接模式允许你组合不同的抽象和实现,并且可以独立地增加新的抽象或新的实现,而无需修改现有代码。
- 当一个类的实现需要在运行时进行选择或切换时。
- 当一个类存在多个维度的变化,并且希望避免类数量的指数级增长时。 比如,图形界面库需要在不同操作系统(Windows、macOS、Linux)上绘制不同的窗口控件(按钮、文本框)。使用桥接模式,可以将“窗口控件”作为抽象,将“操作系统绘制API”作为实现,避免创建
WindowsButton
,MacButton
,LinuxButton
,WindowsTextBox
,MacTextBox
,LinuxTextBox
等大量的类。 - 需要在多个对象间共享实现(可能使用引用计数),并且对客户端隐藏该实现细节时。
优点
- 分离抽象和实现: 这是最核心的优点,使得抽象和实现可以独立演化,互不影响。
- 提高可扩展性: 抽象和实现可以各自独立地扩展,符合开闭原则。增加新的抽象类或实现类都相对容易。
- 客户端代码更简洁: 客户端只需要与抽象类交互,无需关心具体的实现细节。
- 更好的灵活性: 可以在运行时动态地切换实现部分。
- 隐藏实现细节: 客户端看不到具体的实现,只依赖于抽象接口。
缺点
- 增加系统复杂度: 引入了额外的类(Implementor 接口和其实现类),使得系统的结构变复杂,理解成本有所增加。
- 设计难度: 需要在设计初期就准确地识别出合适的抽象和实现维度,并进行分离。如果设计不当,可能达不到预期效果。
- 可能存在一定的性能影响: 由于增加了一层间接调用(抽象委托给实现),可能会带来微小的性能损失(通常可忽略不计)。
示例代码
Simple
Python
from abc import ABCMeta, abstractmethod
class Implementor(metaclass=ABCMeta):
@abstractmethod
def implementation(self):
pass
class ConcreteImplementor(Implementor):
def implementation(self, s: str):
print(f"ConcreteImplementor: {s}")
class RefineAbstraction:
def __init__(self, i: Implementor):
self.method = i
def execute(self, s: str):
self.method.implementation(s)
if __name__ == "__main__":
c = ConcreteImplementor()
r = RefineAbstraction(c)
r.execute("Hello World!")
Go
package bridge
import "fmt"
type Implementor interface {
Implementation(str string)
}
type ConcreteImplementor struct{}
func (*ConcreteImplementor) Implementation(str string) {
fmt.Printf("Implementation: %s\n", str)
}
func NewConcreteImplementor() *ConcreteImplementor {
return &ConcreteImplementor{}
}
type RefinedAbstraction struct {
method Implementor
}
func (c *RefinedAbstraction) Execute(str string) {
c.method.Implementation(str)
}
func NewRefinedAbstraction(im Implementor) *RefinedAbstraction {
return &RefinedAbstraction{method: im}
}
client
package bridge
import "testing"
func TestSimple(t *testing.T) {
c := NewConcreteImplementor()
r := NewRefinedAbstraction(c)
r.Execute("Hello World!")
}
电脑打印机
Python
from abc import ABC, abstractmethod
class Computer(ABC):
@abstractmethod
def print_(self):
pass
@abstractmethod
def set_printer(self, printer):
pass
class Printer(ABC):
@abstractmethod
def print_file(self):
pass
class Mac(Computer):
def __init__(self, printer: Printer):
self.printer = printer
def print_(self):
print("Mac Print")
self.printer.print_file()
def set_printer(self, printer: Printer):
self.printer = printer
class Lenovo(Computer):
def __init__(self, printer: Printer):
self.printer = printer
def print_(self):
print("Lenovo Print")
self.printer.print_file()
def set_printer(self, printer: Printer):
self.printer = printer
class Canon(Printer):
def print_file(self):
print("Printing by a Canon Printer")
class Hp(Printer):
def print_file(self):
print("Printing by a Hp Printer")
# 示例用法
if __name__ == "__main__":
canon_printer = Canon()
hp_printer = Hp()
mac = Mac(canon_printer)
mac.print_() # 输出: Mac Print
# Printing by a Canon Printer
mac.set_printer(hp_printer)
mac.print_() # 输出: Mac Print
# Printing by a Hp Printer
lenovo = Lenovo(hp_printer)
lenovo.print_() # 输出: Lenovo Print
# Printing by a Hp Printer
Go
package bridge
import "fmt"
type Computer interface {
Print()
SetPrinter(Printer)
}
type Printer interface {
PrintFile()
}
type Mac struct {
Printer Printer
}
func (m *Mac) Print() {
fmt.Println("Mac Print")
m.Printer.PrintFile()
}
func (m *Mac) SetPrinter(p Printer) {
m.Printer = p
}
type Lenovo struct {
Printer Printer
}
func (l *Lenovo) Print() {
fmt.Println("Lenovo Print")
l.Printer.PrintFile()
}
func (l *Lenovo) SetPrinter(p Printer) {
l.Printer = p
}
type Canon struct {}
func (c *Canon) PrintFile() {
fmt.Println("Printing by a Canon Printer")
}
type Hp struct {}
func (h *Hp) PrintFile() {
fmt.Println("Printing by a Hp Printer")
}
client
package bridge
import (
"testing"
)
func TestComputer(t *testing.T) {
hpPrinter := &Hp{}
canonPrinter := &Canon{}
mac := &Mac{Printer: hpPrinter}
mac.Print()
mac.SetPrinter(canonPrinter)
mac.Print()
lenovo := &Lenovo{Printer: canonPrinter}
lenovo.Print()
lenovo.SetPrinter(hpPrinter)
lenovo.Print()
}