目录

定义

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

其本质: 转换匹配,复用功能。

示例代码

package main

import "fmt"

// Target 客户端使用的接口
type Target interface {
	request()
}

// Adpatee 已经存在的接口,这个接口需要被适配
type Adaptee struct {
}

func (receiver *Adaptee) specificRequest() {
	fmt.Println("Adaptee Call...")
}

type Adapter struct {
	Adaptee
}

//request 继承Target接口
func (a *Adapter) request() {
	a.Adaptee.specificRequest()
}

func NewAdapter(adaptee Adaptee) Target {
	return &Adapter{adaptee}
}

func main() {
	target := NewAdapter(Adaptee{})
	target.request()
}

场景

日志系统原先使用文件来记录,现在需要使用数据库来记录。

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"
)

//LogModel 日志数据对象类
type LogModel struct {
	LogID       int
	OperateUser string
	OperateTime time.Time
	LogContent  string
}

func (l *LogModel) ToString() string {
	return fmt.Sprintf("LogID:%d OperateUser: %s Time: %s LogContent: %s\n", l.LogID, l.OperateUser, l.OperateTime, l.LogContent)
}

// ILogFileOperate 操作日志文件的接口
type ILogFileOperate interface {
	ReadLogFile() string
	WriteLogFile(model LogModel)
}

//LogFileOperate 日志实现类
type LogFileOperate struct {
	LogFileName string
}

func NewLogFileOperate() *LogFileOperate {
	return &LogFileOperate{LogFileName: "adapter.log"}
}

func (l *LogFileOperate) ReadLogFile() string {
	readFile, err := ioutil.ReadFile(l.LogFileName)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(readFile))
	return string(readFile)
}

func (l *LogFileOperate) WriteLogFile(model LogModel) {
	file, err := os.OpenFile(l.LogFileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	_, _ = file.WriteString(model.ToString())
}

func main() {
	model := LogModel{
		LogID:       001,
		OperateUser: "admin",
		OperateTime: time.Now(),
		LogContent:  "blog.68hub.com ok!",
	}
	LogFile := NewLogFileOperate()
	LogFile.WriteLogFile(model)
	LogFile.ReadLogFile()
}

在以上基础上增加日志保存至数据库中的逻辑,使用Adapter模式

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"
)

//LogModel 日志数据对象类
type LogModel struct {
	LogID       int
	OperateUser string
	OperateTime time.Time
	LogContent  string
}

func (l *LogModel) ToString() string {
	return fmt.Sprintf("LogID:%d OperateUser: %s Time: %s LogContent: %s\n", l.LogID, l.OperateUser, l.OperateTime, l.LogContent)
}

// ILogFileOperate 操作日志文件的接口
type ILogFileOperate interface {
	ReadLogFile() string
	WriteLogFile(model LogModel)
}

// ILogDBOperate 操作日志数据库接口
type ILogDBOperate interface {
	CreateLog(model LogModel)
	UpdateLog(model LogModel)
	RemoveLog(model LogModel)
	GetAllLog() string
}

//LogFileOperate 日志实现类
type LogFileOperate struct {
	LogFileName string
}

func NewLogFileOperate() *LogFileOperate {
	return &LogFileOperate{LogFileName: "adapter.log"}
}

func (l *LogFileOperate) ReadLogFile() string {
	readFile, err := ioutil.ReadFile(l.LogFileName)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(readFile))
	return string(readFile)
}

func (l *LogFileOperate) WriteLogFile(model LogModel) {
	file, err := os.OpenFile(l.LogFileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	_, _ = file.WriteString(model.ToString())
}

type Adapter struct {
	logFileAdaptee ILogFileOperate
}

//CreateLog(model LogModel)
//UpdateLog(model LogModel)
//RemoveLog(model LogModel)
//GetAllLog() string

func (a *Adapter) CreateLog(model LogModel) {
	//先读取文件内容
	file := a.logFileAdaptee.ReadLogFile()
	fmt.Println("Create Log :", file)
	//重写日志文件
	a.logFileAdaptee.WriteLogFile(model)
}

func (a *Adapter) UpdateLog(model LogModel) {
	//先读取文件内容
	file := a.logFileAdaptee.ReadLogFile()
	//修改日志对象
	//...
	//重新写入文件
	a.logFileAdaptee.WriteLogFile(model)
	fmt.Println("Update Log :", file)
}

func (a *Adapter) RemoveLog(model LogModel) {
	//读取文件内容
	a.logFileAdaptee.ReadLogFile()
	//删除对应的日志对象
	//...
	//重新写入文件
	a.logFileAdaptee.WriteLogFile(model)
	fmt.Println("Remove Log :", model.ToString())

}

func (a *Adapter) GetAllLog() string {
	return a.logFileAdaptee.ReadLogFile()
}

func NewAdapter(operate ILogFileOperate) ILogDBOperate {
	return &Adapter{operate}
}

func main() {
	model := LogModel{
		LogID:       002,
		OperateUser: "admin",
		OperateTime: time.Now(),
		LogContent:  "adapter code",
	}
	//创建日志文件对象
	LogFile := NewLogFileOperate()
	//创建新版数据库日志接口对象
	LogDB := NewAdapter(LogFile)

	LogDB.CreateLog(model)
	LogDB.UpdateLog(model)

}

适配器模式的主要功能是进行转换匹配,目的是复用已有的功能,而不是实现新的接口。也就是说,客户端需要的功能应该是已经实现好了的,不需要适配器来实现,适配器模式 主要负责把不兼容的接口转换成客户端期望的样子就可以了。

但这并不是说在适配器里面就不能实现功能。适配器里面可以实现功能。这种称为智能适配器。

Adaptee 和 Target 的关系

在此模式中被适配的接口Adaptee和适配器成为的接口Target是没有关联的,也就是说AdapteeTarget中的方法既可以相同,也可以不同。

总结

适配器的实现方式其实是依靠对象组合的方式。通过给适配器对象组合被适配去的对象,然后当客户端调用Target的时候,适配器会把相应的功能委托给被适配器的对象去完成。

优缺点

优点

  • 更好的复用性
  • 更好的扩展性

缺点

  • 过多的使用适配器,会让系统非常凌乱。