【设计模式】如何用C++实现适配器模式
一、问题背景
用到过很多次适配器模式,一直不理解为什么用这种模式,好像这个模式天生就该如此使用。
实际上,我们很多的理念都源于一些简朴的思想,这些思想不一定高深,但是在保证代码质量,实现高内聚低耦合的设计思想上也许有所益处。
解决问题时,本着好读书不求甚解的思想去做,以求采用有效合适的方式合理解决问题,这没有错。
但是前置学习或者事后复盘时,也许应该知其然知其所以然。学习其思想,解构其体系,重构其内涵,让这种简朴的思想经历复杂的过程后,再次回归简朴。
二、什么是适配器模式?
适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。简单来说,就是将一个类的接口转换成客户希望的另一个接口,使得原本不兼容的类可以一起工作。
适配器模式的核心思想就是通过引入一个中间层(适配器类),来解决不同接口之间的不兼容问题,使得它们能够协同工作。这种模式在软件开发中非常常见,尤其是在需要集成不同系统或框架的时候。
适配器模式有两种常见的实现方式:
- 类适配器模式: 适配器类继承了原有类和目标接口,通过继承来实现接口转换。
- 对象适配器模式: 适配器类持有原有类的实例,通过组合的方式来实现接口转换。
一般情况下使用对象适配器,因为它耦合度更低,更符合面向对象的设计原则。
三、为什么使用适配器模式?
-
解决接口不兼容问题: 当你希望使用某个类,但是其接口与其他代码不兼容时,可以使用适配器类。
-
兼容旧系统: 适配器模式允许你创建一个中间层类,其可作为代码与遗留类、第三方类或提供怪异接口的类之间的转换器。
-
提高代码复用性: 通过适配器,可以将已有的类适配成新的接口,从而在新的系统中重用这些类。
四、实现步骤
有现存接口类AClass
和BClass
,可以定义Adapter
去适配这些旧接口以实现新功能,在main
函数中调用适配器接口来实现自身功能,而无需关注AClass
和BClass
的具体实现。
假设旧接口AFunc1、AFunc2、BFunc1、BFunc2不能满足用户需求,用户需要的是AFunc和BFunc任意组合后的接口,如果将这个接口放在用户调用层去实现的话,用户需要同时持有和管理A对象和B对象,在对象较少的情况下采用这种方式或许可行。
随着需要管理的对象越来越多,用户实现与底层之间的耦合将会越来越深。牵一发而动全身,任何一个底层的修改都可能导致原先的功能出现异常。
而采用适配器模式,我们将接口对象使用适配器管理,针对用户业务场景划分适配器的类别,仅需少许适配器,就可实现用户需要的所有功能。此时所有适配器都是对应用户场景,方便用户理解并且无需关心底层原始实现。
1. 旧接口类someClass
./someClass/AClass.h
#ifndef SOMECLASS_ACLASS_H
#define SOMECLASS_ACLASS_H
namespace SomeClass
{class AClass {public:AClass();~AClass();void AFunc1();void AFunc2();};
}
#endif
./someClass/AClass.cpp
#include "AClass.h"
#include <iostream>using namespace SomeClass;AClass::AClass()
{std::cout << "In AClass, construction" << std::endl;
}AClass::~AClass()
{std::cout << "In AClass, destruction" << std::endl;
}void AClass::AFunc1()
{std::cout << "In AClass, Func1" << std::endl;
}void AClass::AFunc2()
{std::cout << "In AClass, Func2" << std::endl;
}
./someClass/BClass.h
#ifndef SOMECLASS_BCLASS_H
#define SOMECLASS_BCLASS_H
namespace SomeClass
{class BClass {public:BClass();~BClass();void BFunc1();void BFunc2();};
}
#endif
./someClass/BClass.cpp
#include "BClass.h"
#include <iostream>using namespace SomeClass;BClass::BClass()
{std::cout << "In BClass, construction" << std::endl;
}BClass::~BClass()
{std::cout << "In BClass, destruction" << std::endl;
}void BClass::BFunc1()
{std::cout << "In BClass, Func1" << std::endl;
}void BClass::BFunc2()
{std::cout << "In BClass, Func2" << std::endl;
}
2. 对象适配器
./adapter/Adapter.h
#ifndef ADAPTER_H
#define ADAPTER_H
namespace Adapter
{class Adapter {public:virtual void FUNCA1B1() = 0;virtual void FUNCA1B2() = 0;virtual void FUNCA2B1() = 0;virtual void FUNCA2B2() = 0;};
}
#endif
./adapter/AdapterImpl.h
#ifndef ADAPTER_IMPL_H
#define ADAPTER_IMPL_H
#include "Adapter.h"
#include "AClass.h"
#include "BClass.h"
namespace Adapter
{class AdapterImpl : public Adapter {public:AdapterImpl();~AdapterImpl();void FUNCA1B1() override;void FUNCA1B2() override;void FUNCA2B1() override;void FUNCA2B2() override;private:SomeClass::AClass m_aClass;SomeClass::BClass m_bClass;};
}
#endif
./adapter/AdapterImpl.cpp
#include "AdapterImpl.h"
#include <iostream>
using namespace Adapter;
using namespace SomeClass;AdapterImpl::AdapterImpl()
{std::cout << "In AdapterImpl, construction" << std::endl;
}AdapterImpl::~AdapterImpl()
{std::cout << "In AdapterImpl, destruction" << std::endl;
}void AdapterImpl::FUNCA1B1()
{m_aClass.AFunc1();m_bClass.BFunc1();
}void AdapterImpl::FUNCA1B2()
{m_aClass.AFunc1();m_bClass.BFunc2();
}void AdapterImpl::FUNCA2B1()
{m_aClass.AFunc2();m_bClass.BFunc1();
}void AdapterImpl::FUNCA2B2()
{m_aClass.AFunc2();m_bClass.BFunc2();
}
3. main函数调用
./main.cpp
#include "Adapter.h"
#include "AdapterImpl.h"
#include <memory>int main()
{std::shared_ptr<Adapter::Adapter> adapter = std::make_shared<Adapter::AdapterImpl>();adapter->FUNCA1B1();adapter->FUNCA1B2();adapter->FUNCA2B1();adapter->FUNCA2B2();return 0;
}
4. 编写CMakeLists.txt
# 设置项目名称和最低CMake版本
cmake_minimum_required(VERSION 3.10)
set(ProjectName Adapter)
project(${ProjectName})set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)include_directories(.adaptersomeClass
)file(GLOB LIB_FILE adapter/*someClass/*)add_executable(${ProjectName}main.cpp${LIB_FILE})
此时文件树结构如下:
5. 编译运行
mkdir build
cd build
cmake ..
make -j12
./Adapter
运行结果如下
In AClass, construction
In BClass, construction
In AdapterImpl, construction
In AClass, Func1
In BClass, Func1
In AClass, Func1
In BClass, Func2
In AClass, Func2
In BClass, Func1
In AClass, Func2
In BClass, Func2
In AdapterImpl, destruction
In BClass, destruction
In AClass, destruction
在main
函数不感知AClass
具体实现的情况下使用Adapter
对象适配器,同时持有AClass
和BClass
的实例,实现了组合AClass
接口和BClass
接口的功能。