适配器模式是我们在工作中会经常使用到的结构性设计模式。比如对接口升级的同时,又要保证对旧借口的兼容。
场景
生活中我们会遇到这种情况,我们常用的插线板三插孔(10A)与空调插头(16A)的大小不一样,虽然都是三插孔。当我们买了空调,安装时发现空调插头并不能插入我们原有(10A)的插孔时,我们可以买一个插头转换器,如下图。
我们可以把10A的反面插头插入我们正常的10A三插孔,再将空调的插头插入正面的16A三插孔即可。这里,我们的插头转换器扮演的就是 适配器(Adapter)的角色。
接下来我会以这个插头转换器应用为例,以代码的形式表达适配器模式。虽然演示代码很多,但是却很有代表性,请认真阅读哦
基础接口
这里我们先定义三个接口
电器设备(Appliances)接口
定义了一个返回电器名称的方法GetName(),所有家用电器都需要实现这个接口
// 电器设备接口
type Appliances interface {
GetName() string
}
10A插头的电器设备(Appliances10A)接口
组合(Golang没有继承的概念)Appliances接口,并定义了一个方法Plug10A(),用来实现该接口,所有10A插头的电器都需要实现这个接口
// 10A插头电器接口,所有10A插头的电器设备都要实现该接口
type Appliances10A interface {
// 组合电器设备接口
Appliances
// 插头是10A的,实现该接口
Plug10A()
}
16A插头的电器设备(Appliances16A)接口
跟 Appliances10A基本一样,组合Appliances接口,并定义了实现该接口的方法Plug16A(),所有16A插头的电器都需要实现这个接口
// 16A插头电器接口,所有16A插头的电器设备都要实现该接口
type Appliances16A interface {
Appliances
// 插头是16A的,实现该接口
Plug16A()
}
电器设备的实现
10A插头的电器-电视机(TV)
TV是一个10A插头的电器设备,所以TV类型需要实现Appliances的GetName()方法,以及Plug10A()方法
// 电视机
type TV struct {
// 电器名称,比如: 空调,电视,冰箱
Name string
}
func (tv *TV) GetName() string {
return tv.Name
}
// 电视机是10A的插头
func (tv *TV) Plug10A() {}
16A插头的电器-空调(HVAC)
HVAC是一个16A插头的电器设备,所以也实现Appliances的GetName()方法和 Plug16A()方法
// 空调
type HVAC struct {
Name string
}
func (hvac *HVAC) GetName() string {
return hvac.Name
}
// 空调是16A的插头
func (hvac *HVAC) Plug16A() {}
基础设施
标准10A插孔的插线板(PlugBoard)
因为是10A的插孔,所以定义了一个插入 10A插头电器的方法Insert10A(),入参必须是一个 10A插头的电器类型,
// 插线板(10A)
type PlugBoard struct {
// 电器集合
Appliances []Appliances
}
// 插入 10A插头的电器设备
func (p *PlugBoard) Insert10A(app Appliances10A) {
p.Appliances = append(p.Appliances, app)
}
// 插线板通电,那么在插线板上的电器都通上电了
func (p *PlugBoard) TransferCurrent() {
for _, app := range p.Appliances {
fmt.Printf("%s 已通电 \n", app.GetName())
}
}
适配器(Adapter)
Adapter是可以被认为是一个 10A插头的电器设备,所以实现了 Plug10A(),但因为是个转换器,背面还有一个16A的插孔,所以定了一个插入 16A插头电器的方法
// 适配器
type Adapter struct {
// 正面(10A)插入的插线板
PB PlugBoard
// 反面(16A)接入的电器设备
APP Appliances16A
}
// 适配器能够插入插线板,且能通电,所以本身也是一个电器设备
func (a *Adapter) GetName() string{
return a.APP.GetName()
}
// 正面是 10A的插头
func (a *Adapter) Plug10A() {}
// 背面有 16A的插孔,所以可以插入一个16A插头的设备
func (a *Adapter) Insert16A(app Appliances16A) {
a.APP = app
a.PB.Insert10A(a)
}
测试
func main() {
// 电视机
tv := TV{Name: "电视机"}
// 空调
hvac := HVAC{Name: "空调"}
//插线板
pb := PlugBoard{}
// 插线板上插入 电视机插头
pb.Insert10A(&tv)
// 适配器
adapter := Adapter{}
//插线板上插入 适配器插头
pb.Insert10A(&adapter)
// 适配器上插入 空调插头
adapter.Insert16A(&hvac)
// 给插线板通电
pb.TransferCurrent()
}
运行结果
代码已上传Github:LyonNee/design_patterns_with_go: 基于Golang实现的设计模式代码 (github.com)