适配器模式(结构型模式)
模式概述
这是一种结构性设计模式,它使接口不兼容的对象之间能够相互合作。
手机充电头就是典型的适配器,它将中国大陆通行的220V交流电转换为10V左右的直流电,而且还附带了过压保护、过流保护和短路保护等额外功能。
问题
有些爬虫程序可以从不同的网站下载XML格式的数据,然后从这些XML中提取所需要的内容。
但是公司现有的数据库只接受JSON格式的数据,之所以如此,是因为公司购买的商业报表软件只能识别JSON格式的数据,而且还无法修改或扩展。
方案
可以开发一个专用数据格式转换程序,它输入XML格式的数据或文件,输出JSON格式的数据或文件,这样就既能实现数据爬取,又能满足商业报表软件的要求了。
这个专用的转换程序就是一个适配器
。

结构

实现
Java
/**
* 圆孔
*
*/
public class RoundHole {
private double radius;
public RoundHole(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public boolean fits(RoundPeg peg) {
boolean result;
result = (this.getRadius() >= peg.getRadius());
return result;
}
}
/**
* 圆钉
*
*/
public class RoundPeg {
private double radius;
public RoundPeg() {}
public RoundPeg(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
}
/**
* 方钉
*
*/
public class SquarePeg {
private double width;
public SquarePeg(double width) {
this.width = width;
}
public double getWidth() {
return width;
}
public double getSquare() {
double result;
result = Math.pow(this.width, 2);
return result;
}
}
/**
* 方钉到圆孔的适配器
*
*/
public class SquarePegAdapter extends RoundPeg {
private SquarePeg peg;
public SquarePegAdapter(SquarePeg peg) {
this.peg = peg;
}
@Override
public double getRadius() {
double result;
// 计算最小半径是否符合条件
result = (Math.sqrt(Math.pow((peg.getWidth() / 2), 2) * 2));
return result;
}
}
/**
* 客户端
*
*/
public class Client {
public static void main(String[] args) {
RoundHole hole = new RoundHole(5);
RoundPeg rpeg = new RoundPeg(5);
if (hole.fits(rpeg)) {
System.out.println("圆钉5适合圆孔5");
}
SquarePeg smallSqPeg = new SquarePeg(2);
SquarePeg largeSqPeg = new SquarePeg(20);
// 解决适配问题
SquarePegAdapter small = new SquarePegAdapter(smallSqPeg);
SquarePegAdapter large = new SquarePegAdapter(largeSqPeg);
if (hole.fits(small)) {
System.out.println("方钉2适合圆孔5");
}
if (!hole.fits(large)) {
System.out.println("方钉20不适合圆孔5");
}
}
}
Go
package main
import "fmt"
/**
* 计算功能接口
*
*/
type Computer interface {
InsertIntoLightningPort()
}
/**
* Mac计算功能
*
*/
type Mac struct {
}
func (m *Mac) InsertIntoLightningPort() {
fmt.Println("连接器插入到Mac")
}
/**
* Windows计算功能
*
*/
type Windows struct{}
func (w *Windows) insertIntoUSBPort() {
fmt.Println("连接器插入到Windows")
}
/**
* Windows适配器
*
*/
type WindowsAdapter struct {
windowMachine *Windows
}
func (w *WindowsAdapter) InsertIntoLightningPort() {
fmt.Println("适配器将Lightning信号转换为USB信号")
w.windowMachine.insertIntoUSBPort()
}
/**
* 客户端
*
*/
type Client struct {
}
func (c *Client) InsertLightningConnectorIntoComputer(com Computer) {
fmt.Println("客户端插入连接器到计算机")
com.InsertIntoLightningPort()
}
func main() {
client := &Client{}
mac := &Mac{}
client.InsertLightningConnectorIntoComputer(mac)
windowsMachine := &Windows{}
windowsMachineAdapter := &WindowsAdapter{
windowMachine: windowsMachine,
}
client.InsertLightningConnectorIntoComputer(windowsMachineAdapter)
}
适用场景
当应用与应用之间接口不兼容时,可以考虑使用适配器模式。
需要一个可以复用的类,用于一些彼此没有太大关联的类共同工作,包括一些可能在将来引进的类。
优缺点
适配器模式的优点。
将接口或数据转换代码从业务逻辑中分离与解耦,符合
单一职责原则
。将具体的实现封装在适配器类中,增加了适配器类的复用性。
能够在不修改客户端代码的情况下在程序中添加新类型的适配器,符合
开闭原则
。
适配器模式的缺点。
一次只能满足一个适配需求,有一定的局限性。
无法实现动态修改适配器。
相关性
桥接模式
通常会用于开发前期,使程序的各个部分能够独立开来以便开发;而适配器模式
通常在已有程序中使用,让不同的类之间兼容。适配器模式
可以对已有对象的接口进行修改,而修饰器模式
则能在不改变对象接口的前提下强化对象功能。另外,修饰器模式
还支持递归组合,适配器模式
则无法实现。适配器模式
能为被封装对象提供不同的接口,代理模式
能为对象提供相同的接口,修饰器模式
则能为对象提供加强的接口。外观模式
为现有对象定义一个新接口,通常会作用于整个对象子系统上;适配器模式
则会封装已有的接口,且仅封装单个对象。桥接模式
、状态模式
、策略模式
和适配器模式
较为相似,它们都基于组合模式
机制——将工作委派给其他对象来解决问题。
感谢支持
更多内容,请移步《超级个体》。