内容:记录桥接模式的学习
桥接模式:
桥接是用于把抽象化与实现化解偶,使得二者可以独立变化。这种类型的设计模式属于结构型模式,
它通过提供抽象化和实现化之间的桥接结构,来实现二者的解偶。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变
而互不影响。
意图:将抽象部分与实现部分分离,使他们都可以独立的变化。
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让他们独立变化,减少他们之间的耦合。
关键代码:抽象类依赖实现类。
优点:抽象和实现的分离、优秀的扩展能力、实现细节与客户透明
缺点:桥接模式的引用会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,
要求开发者针对抽象进行设计与编程。
使用场景:作用于两个独立变化的纬度
举例:
电器是一个抽象类(灯泡和风扇是它的具体实现类)
开关是另一个抽象类(圆形开关和方形开关是它的具体实现类)
这两个抽象类通过桥接的形式连接在一起(线路),在这种情况下,我们可以替换抽象类中的任意一个而让
整体系统不受到影响。
一个使用桥接与不使用桥接的例子:
在现实生活中,我们画画的时候常常会用到两种或多种类型的笔,比如毛笔和蜡笔。
假设我们需要大、中、小三种类型的画笔来绘制12种不同的颜色:
不使用桥接模式:
相当于我们使用蜡笔,那么我们需要准备3*12=36支蜡笔;
使用桥接模式:
相当于我们使用毛笔,那么我们只需要3种型号的毛笔,外加12个颜料盒即可,涉及的对象个数仅为3+12=15。
差异:
如果新增一种画笔,并且同样需要12种颜色,那么蜡笔需要增加12支,而毛笔却只需要新增1支。
分析:
在蜡笔中,颜色和型号两个不同的变化维度耦合在一起,导致无论对任何一个维度进行扩展,都势必会影响
另外一个维度
在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号都对另外一个维度没有任何影响
通用结构示意图:
上述画笔例子示意图:
桥接模式中的概念:
Abstraction(抽象类,图中的BrushPen接口):
用于定义抽象类的接口,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,
也可以包含具体业务方法。
RefinedAbstratction(扩充抽象类,图中的BigBrushPen和SmallBrushPen):
扩充由Abstraction定义的接口,通常情况下他不再是抽象类而是具体类,实现了在Abstraction
中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
Implementor(实现类接口,图中的Color接口):
定义实现类的接口,一般而言,它不与Abstraction的接口一致。它只提供基本操作,而Abstraction
定义的接口可能会做更多更复杂的操作。
ConcreteImplementor(具体实现类,图中的Red、Green、Yellow):
具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,
ConcreteImplentor将替换其父类对象,提供给抽象类具体的业务操作方法。
上述画笔例子的桥接模式示例:
## 定义Implementor接口及实现:
type Color interface {
Use()
}
type Red struct{}
func (r Red) Use() {
fmt.Println("Use Red color")
}
type Green struct{}
func (g Green) Use() {
fmt.Println("Use Green color")
}
type Yellow struct{}
func (y Yellow) Use() {
fmt.Println("Use Yellow color")
}
## 定义Abstraction及实现:
type BrushPen interface {
DrawPicture()
}
type BigBrushPen struct {
Color
}
func (bbp BigBrushPen) DrawPicture() {
fmt.Println("Draw picture with big brush pen")
bbp.Use()
}
type SmallBrushPen struct {
Color
}
func (sbp SmallBrushPen) DrawPicture() {
fmt.Println("Draw picture with small brush pen")
sbp.Use()
}
## 定义工厂方法生产具体的BrushPen
func NewBrushPen(t string, color Color) BrushPen {
switch t {
case "BIG":
return BigBrushPen{
Color: color,
}
case "SMALL":
return SmallBrushPen{
Color: color,
}
default:
return nil
}
}
## 使用
func main() {
var tColor bridge.Color
tColor = bridge.Red{}
tBrushPen := bridge.NewBrushPen("BIG", tColor)
tBrushPen.DrawPicture()
tColor = bridge.Green{}
tBrushPen = bridge.NewBrushPen("SMALL", tColor)
tBrushPen.DrawPicture()
tColor = bridge.Yellow{}
tBrushPen = bridge.NewBrushPen("BIG", tColor)
tBrushPen.DrawPicture()
}
桥接模式和适配器模式:
桥接模式和适配器模式的区别:
共同点:
桥接和适配器都是让两个模块配合工作
不同点:
适配器:改变已有的两个接口,使之相互兼容
桥接模式:分离抽象化和实现,使两者的接口可以不同,目的是分离。
如何选择:
如果你拿到两个已有模块,想让他们同时工作,那么你使用的适配器。
如果你还什么都没有,但是想分开实现,那么桥接是一个选择。
依赖的先后顺序:
桥接是先有桥,才有两端的东西 适配是先有两边的东西,才有适配器 。
桥接是在桥好了之后,两边的东西还可以变化。
作用的先后顺序:
适配器模式是当发现以前的东西不适用了才去做一个弥补的措施。
桥模式相对来说所做的改变比适配器模式早,它可以适用于有两个甚至两个以上维度的变化。
总结:
1.Bridgen模式使用“对象间的组合关系”解耦了抽象与现实之间固有的绑定关系,使得抽象和实现
可以沿着各自的维度来变化。
2.Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向
的变化维度并不剧烈-换而言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。