[设计模式]结构模式-适配模式(C++描述)
second60 20180429
1. 什么是适配模式
在现有接口中,不修改原有接口的情况下,增加适配器,支持客户的需求。
适配器分为:类适配器模式和对象适配器模式。
现实生活中最简单的例子:手机能电器,我们家用电压为220V(Adaptee),但我们的手机只需12V的电压(Target)来充电。所以中间需要一个电压适配器(Adapter)来转换电压。
概念:
接口继承:通过继承子类获得了父类的接口(子类实现父类接口)
实现继承:通过继承子类获得父类的实现(子类调用父类接口)
在C++中
public继承:即是接口继承,也是实现继承(能重载父类的接口,又获得父类的接口实现)。
private继承:父类中的接口都变为private,只能是实现继承。
纯抽象基类:可以模似接口继承。
2. 适配器模式结构图

类适配模式

对象适配模式
区别:
类适配模式的Adapter采用继承的方式实现Adaptee的接口。
对象模式的Adapter中采用组合的方式实现Adaptee接口。
分析:
1. Target为面向用户的接口
2. Adaptee为原有功能接口
3. Adapter为适配器,在原有的接口上适配新功能
3 代码实现
3.1对象适配模式
#include <iostream>
using namespace std;
// 目标接口
class Target
{
public:
virtual ~Target(){}
virtual void Request()
{
cout << "Target::Request" << endl;
}
};
// 原有接口
class Adaptee
{
public:
Adaptee(){}
~Adaptee(){}
void SpecificRequest()
{
cout << "Adaptee::SpecificRequest" << endl;
}
};
// 适配器
class Adapter: public Target
{
public:
Adapter(Adaptee* adaptee):_adaptee(adaptee){}
~Adapter() override {}
void Request()
{
cout << "do before" << endl;
_adaptee->SpecificRequest();
cout << "do after" << endl;
}
private:
Adaptee* _adaptee;
};
int main()
{
Adaptee *adaptee = new Adaptee();
Target* target = new Adapter(adaptee);
target->Request();
delete target;
delete adaptee;
return 0;
}
3.2 类适配模式
#include <iostream>
using namespace std;
// 目标接口
class Target
{
public:
virtual ~Target(){}
virtual void Request()
{
cout << "Target::Request" << endl;
}
};
// 原有接口
class Adaptee
{
public:
Adaptee(){}
virtual ~Adaptee(){}
void SpecificRequest()
{
cout << "Adaptee::SpecificRequest" << endl;
}
};
//适配器, public 为接口继承和实现继承,private为实现继承
class Adapter: public Target, private Adaptee
{
public:
Adapter(){}
~Adapter() override {}
void Request()
{
cout << "do before" << endl;
Adaptee::SpecificRequest();
cout << "do after" << endl;
}
};
int main()
{
Target *target = new Adapter();
target->Request();
delete target;
return 0;
}
4 适配器的使用场景
适配器模式使用非常广泛,非常常用。
在系统开发中,经常遇上这样的问题:在已经开发好了一套接口,或使用第三方接口,但是接口对于现在需求并不适合,需要通过一定的修改在支持,而不想改变原有的逻辑。此时,适配器就派上了用场。
使用条件:
1. 原有接口不适用客户需求
2. 客户无须只知内部实现,只知道接口。
例1:用户需求,某系统保存玩家信息。用户并不关心你使用什么数据库,只需保存玩家信息即可。设计的时候,一开始采用MYSQL 数据库,但后来又采用ORACLE数据库。这就非常适合用适配器,用Target写一套标准API给用户调用即可,对于接口基本实现由Adapter配置实现。
#include <iostream>
#include <memory> // for smart pointers
using namespace std;
// 1. 目标接口(纯虚函数定义规范)
class DBTarget {
public:
virtual ~DBTarget() {}
// 纯虚函数:强制子类实现
virtual int insert() = 0;
virtual int remove() = 0; // ✅ 避免使用关键字
virtual int update() = 0;
virtual int query() = 0; // ✅ 避免与标准库 select 冲突
};
// 2. 数据库适配器抽象基类
class DBAdaptee {
public:
virtual ~DBAdaptee() {}
virtual int insert() = 0;
virtual int remove() = 0;
virtual int update() = 0;
virtual int query() = 0;
};
// 3. 具体数据库实现
class MysqlAdaptee : public DBAdaptee {
public:
int insert() override {
cout << "MySQL: 插入数据" << endl;
return 0;
}
int remove() override {
cout << "MySQL: 删除数据" << endl;
return 0;
}
int update() override {
cout << "MySQL: 更新数据" << endl;
return 0;
}
int query() override {
cout << "MySQL: 查询数据" << endl;
return 0;
}
};
class OracleAdaptee : public DBAdaptee {
public:
int insert() override {
cout << "Oracle: 插入数据" << endl;
return 0;
}
int remove() override {
cout << "Oracle: 删除数据" << endl;
return 0;
}
int update() override {
cout << "Oracle: 更新数据" << endl;
return 0;
}
int query() override {
cout << "Oracle: 查询数据" << endl;
return 0;
}
};
class Db2Adaptee : public DBAdaptee {
public:
int insert() override {
cout << "DB2: 插入数据" << endl;
return 0;
}
int remove() override {
cout << "DB2: 删除数据" << endl;
return 0;
}
int update() override {
cout << "DB2: 更新数据" << endl;
return 0;
}
int query() override {
cout << "DB2: 查询数据" << endl;
return 0;
}
};
// 4. 适配器类(对象适配器)
class Adaptor : public DBTarget {
public:
// 使用智能指针明确所有权
explicit Adaptor(std::shared_ptr<DBAdaptee> adaptee)
: _adaptee(adaptee) {}
int insert() override {
return _adaptee->insert();
}
int remove() override {
return _adaptee->remove();
}
int update() override {
return _adaptee->update();
}
int query() override {
return _adaptee->query();
}
private:
std::shared_ptr<DBAdaptee> _adaptee;
};
// 5. 工厂类(创建具体数据库对象)
class DBAdapteeFactory {
public:
enum DBType {
MYSQL = 0,
ORACLE,
DB2
};
static std::shared_ptr<DBAdaptee> create(DBType type) {
switch (type) {
case MYSQL: return std::make_shared<MysqlAdaptee>();
case ORACLE: return std::make_shared<OracleAdaptee>();
case DB2: return std::make_shared<Db2Adaptee>();
default: return nullptr;
}
}
};
// 6. 配置类(模拟)
class Config {
public:
static DBAdapteeFactory::DBType db_type;
};
DBAdapteeFactory::DBType Config::db_type = DBAdapteeFactory::MYSQL;
// 7. 主函数
int main() {
// 配置读取(示例)
Config::db_type = DBAdapteeFactory::MYSQL;
// 工厂模式生成 DBAdaptee
auto dbAdaptee = DBAdapteeFactory::create(Config::db_type);
if (!dbAdaptee) {
cerr << "创建数据库适配器失败" << endl;
return -1;
}
// 使用适配器
auto dbTarget = std::make_shared<Adaptor>(dbAdaptee);
dbTarget->query(); // MySQL: 查询数据
dbTarget->update(); // MySQL: 更新数据
dbTarget->insert(); // MySQL: 插入数据
dbTarget->remove(); // MySQL: 删除数据
// 智能指针自动释放内存,无需手动 delete
return 0;
}
分析:
1. 上面DB适配器,无需修改任何代码,只需修改配置,即可支持不同数据库
2. 用户无须关心内部接口的实现
3. 如果需要新增数据库(MongoDB),只需添加MongoDBAdaptee,和工厂支持即可。
其实,上面的DB例子就是通用的DB适配器,对用户做到DB无关性。
5 总结
上面的例子,简单的讲了适配器的例子,想必都清楚适配器的优点了。我就不多说了。设计模式对于面向对象设计是博大精深。
1788



被折叠的 条评论
为什么被折叠?



