引言
在软件开发中,随着项目的不断扩展和需求的增加,代码的可维护性和扩展性往往会面临巨大的挑战。为了应对这些挑战,设计模式作为一种经验总结,提供了很多优秀的解决方案。桥接模式(Bridge Pattern)作为一种结构型设计模式,通过将抽象与实现分离,使得二者可以独立变化,从而在系统中实现更加灵活的扩展和高效的维护。今天,我们将深入探讨桥接模式,了解它的定义、使用场景及实际应用。
什么是桥接模式
桥接模式的目的是把抽象层次结构从其实现中分离出来,使其能够独立变更。抽象层次定义了供客户端使用的上层的抽象接口。实现层次结构定义了供抽象层次使用的底层接口。实现类的引用被封装与抽象层的实例中是,桥接就形成了。
如上图所示:
- Abstraction 定义了供其它类调用的上层抽象接口的父接口,它有一个对Implementor实例的引用imp。
- Implementor定义了实现类的接口。这个接口不比跟Abstraction的接口一致,其实两个接口可以完全没有关系。Implementor的接口提供了基本的操作,而Abstraction的上层操作基于这些基本操作。
- ConcreteImplementator(A或者B),则是Implementor的具体实现类,在它们的operationImp()方法中执行具体的操作。
当我们向Abstraction的实例发送一个operation消息时,也就是调用operation()方法时,这个方法会向imp发送一个operationImp消息,而底下实际的ConcreteImplementator(A或者B)将会做出响应并接受任务。
因此想要往整个系统中添加新的ConcreteImplementator时,所需要做的只是为Implementor创建一个新的实现类,响应operationImp消息并在其中执行任何具体的操作。不过,这对Abstraction不会有任何影响。同样,如果想修改Abstraction的接口或者创建更细化的Abstraction类,也不会对桥接的另一头产生任何影响。
什么场景会使用桥接模式
桥接模式的应用场景主要集中在系统中存在多个变化维度,并且这些维度需要独立扩展的情况下。具体来说,桥接模式通常适用于以下几种情况:
避免固定绑定抽象与实现的关系:
如果你不希望在抽象和其实现之间形成固定的绑定关系,桥接模式可以帮助你在运行时灵活地切换不同的实现方式。
抽象和实现需要独立扩展:
当系统中的抽象和实现都可以通过子类化独立扩展时,桥接模式提供了一种优雅的方式来解耦它们,避免相互依赖。
修改实现不影响客户端代码:
如果你希望修改抽象的实现时,不会影响到客户端代码,桥接模式可以帮助你通过分离的接口和实现,确保系统的灵活性和可维护性。
避免为每个实现创建额外的子类:
如果每个具体的实现都需要额外的子类来细化抽象,那么将抽象和实现分成两部分,并使用桥接模式来管理,可以大大减少代码的重复性。
共享实现给多个具有不同抽象接口的对象:
当多个对象需要共享相同的实现,但又有不同的抽象接口时,桥接模式提供了一种简洁的方式来将这些不同的抽象接口与共享的实现结合起来。
如何使用桥接模式
我们还是以直播间的开发为例,在一个直播应用中,我们可能需要集成多个RTC引擎,如声望、ZGO等等,以备不时之需,有一家出现问题后,我们可以直接切换其它可用的引擎。
每种 RTC 引擎提供不同的实现,而我们希望在直播间中使用这些引擎时,能够灵活切换而不影响其他部分的代码。
我们可以将抽象部分(直播间引擎)与实现部分(具体的 RTC 引擎)分离,通过桥接模式让它们独立变化。
Abstraction(抽象角色)
这是定义了客户端需要的接口,并且通过持有Implementor 的引用来委托工作。在这个例子中,我们创建一个LiveRoom类,它定义了start和end方法,,客户端通过它来启动和结束直播,而不关心具体的 RTC 引擎实现。
// 客户端:选择具体的 RTC 引擎
class LiveRoom {
var engine: LiveRoomEngine
init(engine: LiveRoomEngine) {
self.engine = engine
}
func start() {
engine.startLive()
}
func end() {
engine.endLive()
}
}
Implementor(实现者角色)
这是桥接模式中的接口部分,通常定义了实际的实现方法。我们定义一个LiveRoomEngine协议,它就是Implementor这部分,它声明了startLive和endLive方法,具体的 RTC 引擎会实现这个协议。
// 抽象部分:直播间引擎
protocol LiveRoomEngine {
func startLive()
func endLive()
}
ConcreteImplementor(具体实现者角色)
这是实现了Implementor接口的具体类,它完成实际的功能。我们定义了RongRTC、ZGORTC、CloudRTC三个类,这三个类就是ConcreteImplementor,它们实现LiveRoomEngine协议,并提供了具体的直播功能。
// 具体实现部分:RTC 引擎
class RongRTC: LiveRoomEngine {
func startLive() {
print("使用声望 RTC 开始直播")
}
func endLive() {
print("使用声望 RTC 结束直播")
}
}
class ZGORTC: LiveRoomEngine {
func startLive() {
print("使用 ZGO RTC 开始直播")
}
func endLive() {
print("使用 ZGO RTC 结束直播")
}
}
class CloudRTC: LiveRoomEngine {
func startLive() {
print("使用云信 RTC 开始直播")
}
func endLive() {
print("使用云信 RTC 结束直播")
}
}
客户端使用
我们在使用时,根据条件来创建不同的直播间引擎。
// 使用不同的 RTC 引擎
let liveRoom1 = LiveRoom(engine: RongRTC())
liveRoom1.start() // 使用声望 RTC 开始直播
liveRoom1.end() // 使用声望 RTC 结束直播
let liveRoom2 = LiveRoom(engine: ZGORTC())
liveRoom2.start() // 使用 ZGO RTC 开始直播
liveRoom2.end() // 使用 ZGO RTC 结束直播
在这个例子中,我们通过桥接模式将直播间的抽象(LiveRoomEngine)和实现(不同的 RTC 引擎)分离开来。当我们需要切换 RTC 引擎时,只需传入不同的实现类,而不需要修改 LiveRoom 类的代码。
结语
桥接模式通过将抽象和实现分离,使得它们可以独立变化,极大地提升了系统的灵活性和可扩展性。在我们的直播应用示例中,通过桥接模式,我们能够轻松切换不同的 RTC 引擎,而不需要修改客户端代码或是重新编写多个子类。这种解耦方式不仅减少了代码的耦合度,还使得系统在面对新的需求或引擎时,可以更加高效地进行扩展。
虽然桥接模式并非适用于所有场景,但在需要多维度扩展和维护的系统中,它无疑是一个强有力的工具。希望通过本文的介绍,能帮助你在实际开发中灵活运用桥接模式,构建更加高效和可维护的系统。