【Adapter模式】C++设计模式——适配器

适配器模式是一种设计模式,用于使不兼容接口的对象能够协同工作。在C++中,适配器可以是对象适配器或类适配器,通过封装或继承来转换接口。本文通过一个股票市场监测程序的例子解释了适配器如何解决数据格式不兼容问题,并提供了代码实现。适配器模式在遗留代码复用、类库迁移等方面十分有用。
摘要由CSDN通过智能技术生成


    C++设计模式大全,23种设计模式合集详解—👉(点我跳转)

一、设计流程探讨

  首先放一张图,让大家大致了解什么叫适配器。适配器属于接口隔离的一种,它能使接口不兼容的对象能够相互合作。
在这里插入图片描述
  假如你正在开发一款股票市场监测程序,它会从不同来源下载 XML 格式的股票数据,然后向用户呈现出美观的图表。
  在开发过程中,你决定在程序中整合一个第三方智能分析函数库。但是遇到了一个问题,那就是分析函数库只兼容 JSON 格式的数据。
在这里插入图片描述
  你可以修改程序库来支持 XML。但是,这可能需要修改部分依赖该程序库的现有代码。甚至还有更糟糕的情况,你可能根本没有程序库的源代码,从而无法对其进行修改。
  你可以创建一个适配器。这是一个特殊的对象,能够转换对象接口,使其能与其他对象进行交互。
  适配器模式通过封装对象将复杂的转换过程隐藏于幕后。被封装的对象甚至察觉不到适配器的存在。例如,你可以使用一个将所有数据转换为英制单位 (如英尺和英里) 的适配器封装运行于米和千米单位制中的对象。
  适配器不仅可转换不同格式的数据,其还助于采用不同接口的对象之间的合作。运作方式如下:
1 - 适配器实现与其中一个现有对象兼容的接口。
2 - 现有对象可以使用该接口安全地调用适配器方法。
3 - 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。

  有时你甚至可以创建一个双向适配器来实现双向转换调用。
在这里插入图片描述
  让我们回到股票市场程序。为了解决数据格式不兼容的问题,你可以为分析函数库中的每个类创建将 XML 转换为 JSON 格式的适配器,然后让客户端仅通过这些适配器来与函数库进行交流。当某个适配器被调用时,它会将传入的 XML 数据转换为 JSON 结构,并将其传递给被封装分析对象的相应方法。
  适配器有两种,分别是对象适配器、类适配器,一般采用对象适配器。
对象适配器的类图
在这里插入图片描述
  类适配器使用了继承机制:适配器同时继承两个对象的接口。请注意,这种方式仅能在支持多重继承的编程语言中实现,例如 C++。
类适配器类图
在这里插入图片描述


真实世界类比:
在这里插入图片描述
  如果你是第一次从美国到欧洲旅行,那么在给笔记本充电时可能会大吃一惊。不同国家的电源插头和插座标准不同。美国插头和德国插座不匹配。同时提供美国标准插座和欧洲标准插头的电源适配器可以解决你的难题。

二、模式介绍

(1)模式动机
  在软件系统中,由于应用环境的变化,常常需要将 “一些现存的对象” 放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
  如何应对这种 “迁移的变化” ?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?
(2)模式定义
  将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
(3)要点总结
a). Adapter模式主要应用于 “希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
b). GoF 23 定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用 “多继承” 的实现方式,一般不推荐使用。对象适配器采用 “对象组合” 的方式,更符合松耦合特点。
c). Adapter模式可以实现的非常灵活,不必拘泥于 GoF23 中定义的两种结构。例如,完全可以将Adapter模式中的 “现存对象” 作为新的接口方法参数,来达到适配的目的。

三、代码实现

  下面将使用代码讲解适配器,Target是未来的接口,Adaptee是被适配者(即遗留的接口)
在这里插入图片描述

//目标接口(新接口)
class ITarget{
public:
	virtual void process() = 0;
};
//遗留接口(老接口)
class IAdaptee{
public:
	virtual void foo(int data) = 0;
	virtual void bar() = 0;
};
//遗留类型
class OldClass : public IAdaptee{
	//...
};
class Adapter : public ITarget{			//继承
protected:
	IAdaptee* pAdaptee;					//组合对象
public:
	Adapter(IAdaptee* pAdaptee){
		this->pAdaptee = pAdaptee;
	}
	virtual void process(){
		int data = pAdaptee->bar();
		pAdaptee->foo(data);
	}
};

int main(){
	IAdaptee* pAdaptee = new OldClass();
	ITarget* pTarget = new Adapter(pAdaptee );
	pTarget->process();
}

  实际运用中,像STL源码的 stack 和 queue的代码,内部都使用了deque,这个deque相当于内部的一个Adapter,虽然没有明确的感觉谁新谁旧,但是就是用deque转成需要的stack或queue。当然他们这部分代码没用指针,是直接将deque对象放上去(如下伪码)。这时候我们并没看到stack、queue的接口是什么,它们的继承是什么,因为它们本身既是接口也是实现,且符合适配器的原则即可将老的接口转为新的接口。

class stack{
	deque container;
}
class queue{
	deque container;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ac君

在你们的鼓励下我会多多分享代码

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值