一、桥模式解决问题的动机
由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个维度的变化.
那么我们应该如何应对"多维度的变化"?如何利用面向对象技术来使得类型可以轻松沿着两个乃至多个方向变化,而不引入额外的复杂度?
二、模式场景
这次的场景假设是设计一个Messager场景,我们设计以下类:
class Messager{
func Login(){}//登陆
func SendMessage(){}//发送消息
func SendPicture(){}//发送图片
func PlaySound(){}//播放音效
func DrawShape(){}//绘制图形
func WriteText(){}//写入信息
func Connect(){}//链接网络
}
然后我们接着为Messager延伸至PC平台、Mobile平台。对于这两个平台,他们之间对于播放音效、绘制图形、写入信息、链接网络的方式会有所不同。于是,我们这样设计.
//平台实现
class PCMessagerBase:Messager{
override func PlaySound() {...}
override func DrawShape(){...}
override func WriteText(){...}
override func Connect(){...}
}
class MobileMessagerBase:Messager{
override func PlaySound() {...}
override func DrawShape(){...}
override func WriteText(){...}
override func Connect(){...}
}
然后最后我们需要再为每个平台设计经典版和完美版,对于这两个版本,他们在登陆上、发送消息和图片上的方式又做出了不同的修改,代码如下:
//业务抽象 PC经典版
class PCMessgaerLite:PCMessagerBase{
override func Login(){
super.Connect()
//...
}
override func SendMessage(){
super.WriteText()
//...
}
override func SendPicture(){
super.DrawShape()
//...
}
}
//PC完美版
class PCMessgaerPerfect:PCMessagerBase{
override func Login(){
super.PlaySound()
super.Connect()
//...
}
override func SendMessage(){
super.PlaySound()
super.WriteText()
//...
}
override func SendPicture(){
super.PlaySound()
super.DrawShape()
//...
}
}
class MobileMessagerLite:MobileMessagerBase{
//...
}
class MobileMessagerPerfect:MobileMessagerBase{
//...
}
这次的例子中,我们很明显发现平台扩展和业务扩展是沿2种不同的方向再延伸.于是假设这次有m个平台扩展,n个业务扩展,那总共需要1+m+m*n.
三、修改设计
根据上一个装饰模式的经验,我们很快会发现经典版和完美版这样的对于手机继承类的子类扩展完全是重复的,于是我们像上一次修改,将编译时多态变为运行时多态。
//业务抽象 PC经典版
class MessgaerLite:Messager{
var messager:Messager?
init(m_messager : Messager){
self.messager = m_messager;
}
override func Login(){
messager?.Connect()
//...
}
override func SendMessage(){
messager?.WriteText()
//...
}
override func SendPicture(){
messager?.DrawShape()
//...
}
}
class MessgaerPerfect:Messager{
var messager:Messager?
init(m_messager : Messager){
self.messager = m_messager;
}
override func Login(){
messager?.PlaySound()
messager?.Connect()
//...
}
override func SendMessage(){
messager?.PlaySound()
messager?.WriteText()
//...
}
override func SendPicture(){
messager?.PlaySound()
messager?.DrawShape()
//...
}
}
尽管这样的写法目前还算OK,但由于功能上朝业务和平台两个维度的扩展,我们其实可以专门设计一个MessagerImp类来应付业务的扩展.写法如下:
class Messager{
var messager:MessagerImp?
func Login(){}
func SendMessage(){}
func SendPicture(){}
}
class MessagerImp{
func PlaySound(){}
func DrawShape(){}
func WriteText(){}
func Connect(){}
}
然后之前的平台代码也可以改为继承MessagerImp.
class PCMessagerBase:MessagerImp{
override func PlaySound() {}
override func DrawShape(){}
override func WriteText(){}
override func Connect(){}
}
class MobileMessagerBase:MessagerImp{
override func PlaySound() {}
override func DrawShape(){}
override func WriteText(){}
override func Connect(){}
}
最后也是桥模式最重要的修改在于业务代码的修改:
class MessgaerLite:Messager{
init(m_messager : MessagerImp){
self.messager = m_messager;
}
override func Login(){
messager?.Connect()
//...
}
override func SendMessage(){
messager?.WriteText()
//...
}
override func SendPicture(){
messager?.DrawShape()
//...
}
}
目前为止桥模式已经演示完毕了,可以发现它和装饰模式十分类似,不同的是,相较于装饰模式,它延伸了对多维度变化的对策.
中间的转变四、总结
1.桥模式使用"对象间的组合关系"解耦了抽象和实现之间固有的绑定关系,是的抽象和实现可以沿着各自的维度来变化.所谓抽象和实现这各自维度的变化
2.桥模式有时候类似于多继承方案,但多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差.桥模式是比多继承方案更好地解决方法
3.桥模式的应用一般在"两个非常强的变化维度",有时一个类也有多于两个的变化维度,这时可以使用桥的扩展模式。
网友评论