设计二十三式之适配器
01 意图
适配器是一种结构设计模式,它允许具有不兼容接口的对象进行写作。
02 问题
想象一下,您正在创建一个股票市场监控应用程序。该应用程序以 XML 格式从多个来源下载股票数据,然后为用户显示漂亮的图表和图表。
在某些时候,您决定通过集成智能的 3rd 方分析库来改进应用程序。但有一个问题:分析库仅适用于 JSON 格式的数据。
您可以更改库以使用 XML。但是,这可能会破坏一些依赖于该库的现有代码。更糟糕的是,您可能一开始就无法访问该库的源代码,从而使这种方法变得不可能。
03 解决方案
您可以创建一个适配器。这是一个特殊的对象,它转换一个对象的接口,以便另一个对象可以理解它。
适配器包装其中一个对象以隐藏在幕后发生的转换的复杂性。被包装的对象甚至不知道适配器。例如,您可以使用将所有数据转换为英制单位(例如英尺和英里)的适配器包装以米和公里为单位的对象。
适配器不仅可以将数据转换为各种格式,还可以帮助具有不同接口的对象进行协作。以下是它的工作原理:
适配器获得一个与现有对象之一兼容的接口。
使用此接口,现有对象可以安全地调用适配器的方法。
接收到调用后,适配器将请求传递给第二个对象,但采用第二个对象所期望的格式和顺序。
有时甚至可以创建一个双向适配器,可以双向转换呼叫。
让我们回到我们的股市应用程序。为了解决不兼容格式的困境,您可以为您的代码直接使用的分析库的每个类创建 XML 到 JSON 适配器。然后调整代码以仅通过这些适配器与库进行通信。当适配器接收到调用时,它将传入的 XML 数据转换为 JSON 结构,并将调用传递给包装分析对象的适当方法。
04 举个栗子
当您第一次从美国旅行到欧洲时,您可能会在尝试为笔记本电脑充电时感到惊讶。不同国家的电源插头和插座标准不同。这就是为什么您的美国插头不适合德国插座的原因。该问题可以通过使用具有美式插座和欧式插头的电源插头适配器来解决。
05 结构实现
对象适配器:
该实现使用对象组合原理:适配器实现一个对象的接口,包装另一个对象。它可以用所有流行的编程语言实现。
类适配器:
此实现使用继承:适配器同时从两个对象继承接口。请注意,这种方法只能在支持多重继承的编程语言中实现,例如 C++。
06 伪代码
这个适配器模式的例子是基于方钉和圆孔之间的经典冲突。
Adapter 伪装成一个圆形钉子,半径等于正方形直径的一半(换句话说,可以容纳方形钉子的最小圆的半径)。
// Say you have two classes with compatible interfaces:
// RoundHole and RoundPeg.
class RoundHole is
constructor RoundHole(radius) { ... }
method getRadius() is
// Return the radius of the hole.
method fits(peg: RoundPeg) is
return this.getRadius() >= peg.getRadius()
class RoundPeg is
constructor RoundPeg(radius) { ... }
method getRadius() is
// Return the radius of the peg.
// But there's an incompatible class: SquarePeg.
class SquarePeg is
constructor SquarePeg(width) { ... }
method getWidth() is
// Return the square peg width.
// An adapter class lets you fit square pegs into round holes.
// It extends the RoundPeg class to let the adapter objects act
// as round pegs.
class SquarePegAdapter extends RoundPeg is
// In reality, the adapter contains an instance of the
// SquarePeg class.
private field peg: SquarePeg
constructor SquarePegAdapter(peg: SquarePeg) is
this.peg = peg
method getRadius() is
// The adapter pretends that it's a round peg with a
// radius that could fit the square peg that the adapter
// actually wraps.
return peg.getWidth() * Math.sqrt(2) / 2
// Somewhere in client code.
hole = new RoundHole(5)
rpeg = new RoundPeg(5)
hole.fits(rpeg) // true
small_sqpeg = new SquarePeg(5)
large_sqpeg = new SquarePeg(10)
hole.fits(small_sqpeg) // this won't compile (incompatible types)
small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)
large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)
hole.fits(small_sqpeg_adapter) // true
hole.fits(large_sqpeg_adapter) // false
07 优缺点
关注公众号可获取干货资料:《设计模式详解》