XingPiaoLiang's

Back

适配器模式#

适配器模式是一种结构性模式。适配器允许我们创建一个中间层类,其可作为代码和第三方类或者提供怪异接口的类之间的转换器。她能够让接口不兼容的对象之间能够相互合作。

问题引入#

现在我们正在开发一个将股票数据给用户可视化为美观的图表形式的股市观测程序。股票数据的来源于一个接口,该接口只会返回XML格式的股票数据。但是我们现在需要使用的可视化工具库,他仅仅兼容JSON格式的数据。

这是一个非常常见的场景,我们无法改动第三方接口(股票数据接口)传递的数据格式。此时我们就需要加上一层中间层,那就是适配器。

问题解决#

适配器,是一个特殊的对象,它能够转换一个对象的接口,让另一个对象能够理解该对象的请求。一个好的适配器,不止将数据转换为不同格式,并且能够帮助有着不同的接口的对象更好的协同工作。

  1. 适配器实现一个适配某一个对象的接口
  2. 使用该接口,存在的对象可以安全的调用适配器中提供所有的方法
  3. 接收到一个对象的请求之后,适配器将请求中的不兼容的内容进行转换,传递给目标对象

在这个股票程序中,我们需要做的就是,创建一个适配器对象(组),可以是为可视化工具库中的每一个类创建,也可以单独创建一个。之后只需要通过适配器与库中方法进行交流,这样就完成了数据格式转换。整体结构如下:

说白了,其实就是没有加一层解决不了的东西

代码示例#

使用经典的图钉适配问题

// 目标接口
type PegAndHole interface {
	getRadius() float32
}

type RoundPeg struct {
	radius float32
}

type RoundHole struct {
	radius float32
}

func Fit(peg PegAndHole, hole PegAndHole) bool {
	return peg.getRadius() >= hole.getRadius()
}

func (r *RoundPeg) getRadius() float32 {
	return r.radius
}

func (r *RoundHole) getRadius() float32 {
	return r.radius
}

type SquarePeg struct {
	width float32
}

// 适配器 “继承” 已有类,来实现目标接口
type SquarePegAdapter struct {
	Peg SquarePeg
}

func (r *SquarePegAdapter) getRadius() float32 {
	radius := r.Peg.width * float32(math.Sqrt(2)) / 2
	return radius
}

func Test(t *testing.T) {
	hole := &RoundHole{
		radius: 20,
	}
	roundPeg := &RoundPeg{
		radius: 30,
	}
	squarePeg := &SquarePeg{
		width: 5,
	}
	adapter := &SquarePegAdapter{
		Peg: *squarePeg,
	}

	if Fit(roundPeg, hole) {
		fmt.Println("roundPeg fit!!")
	} else {
		fmt.Println("roundPeg not fit!!")
	}

	if Fit(adapter, hole) {
		fmt.Println("squarePeg fit!!")
	} else {
		fmt.Println("squarePeg not fit!!")
	}
}
go

How-to-Implement#

  1. Make sure that you have at least two classes with incompatible interfaces:

  • A useful service class, which you can’t change (often 3rd-party, legacy or with lots of existing dependencies).

  • One or several client classes that would benefit from using the service class. Declare the client interface and describe how clients communicate with the service.

  1. Create the adapter class and make it follow the client interface. Leave all the methods empty for now.

  2. Add a field to the adapter class to store a reference to the service object. The common practice is to initialize this field via the constructor, but sometimes it’s more convenient to pass it to the adapter when calling its methods.

  3. One by one, implement all methods of the client interface in the adapter class. The adapter should delegate most of the real work to the service object, handling only the interface or data format conversion.

  4. Clients should use the adapter via the client interface. This will let you change or extend the adapters without affecting the client code.

设计模式-结构型-适配器
https://astro-pure.js.org/blog/adapter
Author erasernoob
Published at May 21, 2025
Comment seems to stuck. Try to refresh?✨