桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
在桥接模式结构图中包含如下几个角色:
-
Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个 Implementor(实现类接口)类型的对象并可以维护该对象,它与 Implementor 之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
-
RefinedAbstraction(扩充抽象类):扩充由 Abstraction 定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在 Abstraction 中声明的抽象业务方法,在 RefinedAbstraction 中可以调用在 Implementor 中定义的业务方法。
-
Implementor(实现类接口):定义实现类的接口,这个接口不一定要与 Abstraction 的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor 接口仅提供基本操作,而 Abstraction 定义的接口可能会做更多更复杂的操作。Implementor 接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在 Abstraction 中不仅拥有自己的方法,还可以调用到 Implementor 中定义的方法,使用关联关系来替代继承关系。
-
ConcreteImplementor(具体实现类):具体实现 Implementor 接口,在不同的 ConcreteImplementor 中提供基本操作的不同实现,在程序运行时,ConcreteImplementor 对象将替换其父类对象,提供给抽象类具体的业务操作方法。
应用场景
假设我们开了一家咖啡店,用户可以选择不同大小的咖啡,可以选择加奶或者加糖,如果用普通的继承结构会变成如下的样子。
这时客户要求还想要加蜂蜜的咖啡,有的客户又想要特大号咖啡,随着种类的增加类的总类也在增加,最后会发现我们会有杯子大小种类x配料种类的个数的类。
为了解决会出现如此多类的问题,这里是用桥接模式来简化结构。
其中ICoffee作为抽象类,提供了OrderCoffee
的方法, 并保有一个实现类方法的实例,具体实现方法大杯(LargeCoffee
)和小杯(SmallCoffee
)分别实现了自己的OrderCoffee
方法,配料Sugar和Milk则作为具体实现类被附在抽象类上
具体实现
package bridge
import "fmt"
type ICoffee interface {
OrderCoffee(count int)
}
type LargeCoffee struct {
additives ICoffeeAdditives
}
func (l *LargeCoffee) OrderCoffee(count int) {
fmt.Print("Large Coffee ")
l.additives.AddAdditives()
fmt.Println(count, " cup")
}
type SmallCoffee struct {
additives ICoffeeAdditives
}
func (s *SmallCoffee) OrderCoffee(count int) {
fmt.Print("Small Coffee ")
s.additives.AddAdditives()
fmt.Println(count, "cup")
}
type ICoffeeAdditives interface {
AddAdditives()
}
type Milk struct{}
func (m *Milk) AddAdditives() {
fmt.Print("With Milk ")
}
type Sugar struct{}
func (s *Sugar) AddAdditives() {
fmt.Print("With Sugar ")
}
测试代码
package bridge
import (
"testing"
)
func TestPrintAPI1(t *testing.T) {
largeCoffeeMilk := LargeCoffee{
additives: &Milk{},
}
largeCoffeeMilk.OrderCoffee(1)
smallCoffeeMilk := SmallCoffee{
additives: &Milk{},
}
smallCoffeeMilk.OrderCoffee(1)
largeCoffeeSugar := LargeCoffee{
additives: &Sugar{},
}
largeCoffeeSugar.OrderCoffee(1)
smallCoffeeMSugar := SmallCoffee{
additives: &Sugar{},
}
smallCoffeeMSugar.OrderCoffee(1)
}
=== RUN TestPrintAPI1
Large Coffee With Milk 1 cup
Small Coffee With Milk 1 cup
Large Coffee With Sugar 1 cup
Small Coffee With Sugar 1 cup
--- PASS: TestPrintAPI1 (0.00s)
PASS