C++适配器模式之可插入适配器的实现模式和方法
可插入适配器与Adaptee的窄接口
在C++适配器模式中,可插入适配器(Pluggable Adapter)是指适配器类的设计允许在运行时动态地插入不同的Adaptee对象,从而使适配器具有灵活性和可扩展性。这种设计使得适配器不仅限于适配一个特定的Adaptee,而是可以适配多个不同的Adaptee。
Adaptee的窄接口(Narrow Interface)是指Adaptee提供的接口只包含有限的方法,这些方法可能不足以满足客户端的需求。适配器的工作就是将这些有限的方法适配成客户端所期望的更丰富的接口。
可插入适配器的实现方式
可插入适配器有以下几种实现方式:
- 对象适配器模式(Object Adapter):适配器持有Adaptee实例的引用。
- 类适配器模式(Class Adapter):适配器通过多重继承同时继承目标接口和Adaptee。
- 双适配器模式(Two-Way Adapter):适配器可以同时作为Adaptee和Target接口的适配器。
下面分别介绍这三种实现方式,并给出UML图和C++代码示例。
1. 对象适配器模式(Object Adapter)
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|
| - adaptee: Adaptee|
|-------------------|
| + request() |
|-------------------|
| + doSomething() |
+-------------------+
|
|
V
+-------------------+
| Adaptee |
|-------------------|
| + doSomething() |
+-------------------+
C++ 代码示例
#include <iostream>
// Target 接口
class Target {
public:
virtual void request() const = 0;
};
// Adaptee 类
class Adaptee {
public:
void doSomething() const {
std::cout << "Adaptee: Doing something." << std::endl;
}
};
// Adapter 类
class Adapter : public Target {
private:
Adaptee* adaptee;
public:
Adapter(Adaptee* adaptee) : adaptee(adaptee) {}
void request() const override {
adaptee->doSomething();
}
};
// 客户端代码
int main() {
Adaptee* adaptee = new Adaptee();
Target* adapter = new Adapter(adaptee);
adapter->request(); // 调用适配器的方法
delete adaptee;
delete adapter;
return 0;
}
2. 类适配器模式(Class Adapter)
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|
| + doSomething() |
|-------------------|
+-------------------+
^
|
|
+-------------------+
| Adaptee |
|-------------------|
| + doSomething() |
+-------------------+
C++ 代码示例
#include <iostream>
// Target 接口
class Target {
public:
virtual void request() const = 0;
};
// Adaptee 类
class Adaptee {
public:
void doSomething() const {
std::cout << "Adaptee: Doing something." << std::endl;
}
};
// Adapter 类
class Adapter : public Target, private Adaptee {
public:
void request() const override {
doSomething();
}
};
// 客户端代码
int main() {
Target* adapter = new Adapter();
adapter->request(); // 调用适配器的方法
delete adapter;
return 0;
}
3. 双适配器模式(Two-Way Adapter)
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|
| + doSomething() |
|-------------------|
+-------------------+
^
|
|
+-------------------+
| Adaptee |
|-------------------|
| + doSomething() |
+-------------------+
C++ 代码示例
#include <iostream>
// Target 接口
class Target {
public:
virtual void request() const = 0;
};
// Adaptee 接口
class Adaptee {
public:
virtual void doSomething() const = 0;
};
// 具体的 Adaptee 类
class ConcreteAdaptee : public Adaptee {
public:
void doSomething() const override {
std::cout << "ConcreteAdaptee: Doing something." << std::endl;
}
};
// Adapter 类
class Adapter : public Target, public Adaptee {
private:
ConcreteAdaptee* adaptee;
public:
Adapter(ConcreteAdaptee* adaptee) : adaptee(adaptee) {}
void request() const override {
adaptee->doSomething();
}
void doSomething() const override {
adaptee->doSomething();
}
};
// 客户端代码
int main() {
ConcreteAdaptee* adaptee = new ConcreteAdaptee();
Adapter* adapter = new Adapter(adaptee);
adapter->request(); // 调用适配器的方法
adapter->doSomething(); // 调用 Adaptee 的方法
delete adaptee;
delete adapter;
return 0;
}
总结
可插入适配器和Adaptee的窄接口是适配器模式的两个重要概念。通过对象适配器、类适配器和双适配器这三种实现方式,适配器可以灵活地适配不同的Adaptee对象,解决接口不兼容的问题。这些实现方式各有优缺点,选择哪种方式取决于具体的需求和设计考虑。
在窄接口实现中,可以通过抽象实现、使用代理对象和参数化适配器方式来实现适配器模式。下面分别说明这三种方式,并给出相应的UML图和C++代码示例。
1. 抽象实现(Abstract Implementation)
抽象实现通过定义一个抽象基类来提供Adaptee的窄接口,然后具体的Adaptee实现类从这个抽象基类继承。适配器类可以持有这个抽象基类的引用,从而在运行时适配不同的具体实现。
UML 类图
+-------------------+ +-------------------+ +-------------------+
| Target | | Adapter | | Adaptee1 |
|-------------------| |-------------------| |-------------------|
| + request() |<----------|>+ request() |<----------|>+ doSomething() |
+-------------------+ |-------------------| +-------------------+
| - adaptee: Adaptee|
|-------------------|
+-------------------+
^
|
|
+-------------------+ +-------------------+
| AbstractAdaptee | | Adaptee2 |
|-------------------| |-------------------|
| + doSomething() |<----------|>+ doSomething() |
+-------------------+ +-------------------+
C++ 代码示例
#include <iostream>
// Target 接口
class Target {
public:
virtual void request() const = 0;
};
// 抽象的 Adaptee 基类
class AbstractAdaptee {
public:
virtual void doSomething() const = 0;
};
// 具体的 Adaptee 实现类 1
class Adaptee1 : public AbstractAdaptee {
public:
void doSomething() const override {
std::cout << "Adaptee1: Doing something." << std::endl;
}
};
// 具体的 Adaptee 实现类 2
class Adaptee2 : public AbstractAdaptee {
public:
void doSomething() const override {
std::cout << "Adaptee2: Doing something." << std::endl;
}
};
// Adapter 类
class Adapter : public Target {
private:
AbstractAdaptee* adaptee;
public:
Adapter(AbstractAdaptee* adaptee) : adaptee(adaptee) {}
void request() const override {
adaptee->doSomething();
}
};
// 客户端代码
int main() {
AbstractAdaptee* adaptee1 = new Adaptee1();
AbstractAdaptee* adaptee2 = new Adaptee2();
Target* adapter1 = new Adapter(adaptee1);
Target* adapter2 = new Adapter(adaptee2);
adapter1->request(); // 调用适配器的方法
adapter2->request(); // 调用适配器的方法
delete adaptee1;
delete adaptee2;
delete adapter1;
delete adapter2;
return 0;
}
2. 使用代理对象(Proxy Object)
代理对象是通过创建一个代理类来间接访问Adaptee的方法。适配器类持有这个代理对象的引用,并通过代理对象来调用Adaptee的方法。这种方式可以提供额外的控制和功能,比如访问控制、缓存等。
UML 类图
+-------------------+ +-------------------+ +-------------------+
| Target | | Adapter | | Proxy |
|-------------------| |-------------------| |-------------------|
| + request() |<----------|>+ request() |<----------|>+ doSomething() |
+-------------------+ |-------------------| +-------------------+
| - proxy: Proxy |
|-------------------|
+-------------------+
^
|
|
+-------------------+ +-------------------+
| Proxy | | Adaptee |
|-------------------| |-------------------|
| + doSomething() |<----------|>+ doSomething() |
+-------------------+ +-------------------+
C++ 代码示例
#include <iostream>
// Target 接口
class Target {
public:
virtual void request() const = 0;
};
// Adaptee 类
class Adaptee {
public:
void doSomething() const {
std::cout << "Adaptee: Doing something." << std::endl;
}
};
// Proxy 类
class Proxy {
private:
Adaptee* adaptee;
public:
Proxy(Adaptee* adaptee) : adaptee(adaptee) {}
void doSomething() const {
std::cout << "Proxy: Before calling Adaptee." << std::endl;
adaptee->doSomething();
std::cout << "Proxy: After calling Adaptee." << std::endl;
}
};
// Adapter 类
class Adapter : public Target {
private:
Proxy* proxy;
public:
Adapter(Proxy* proxy) : proxy(proxy) {}
void request() const override {
proxy->doSomething();
}
};
// 客户端代码
int main() {
Adaptee* adaptee = new Adaptee();
Proxy* proxy = new Proxy(adaptee);
Target* adapter = new Adapter(proxy);
adapter->request(); // 调用适配器的方法
delete adaptee;
delete proxy;
delete adapter;
return 0;
}
3. 参数化适配器(Parameterized Adapter)
参数化适配器通过将Adaptee的方法参数化,使得适配器可以在运行时传递不同的参数来调用Adaptee的方法。这种方式可以增强适配器的灵活性。
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|
| - adaptee: Adaptee|
|-------------------|
+-------------------+
^
|
|
+-------------------+
| Adaptee |
|-------------------|
| + doSomething(int)|
+-------------------+
C++ 代码示例
#include <iostream>
// Target 接口
class Target {
public:
virtual void request() const = 0;
};
// Adaptee 类
class Adaptee {
public:
void doSomething(int param) const {
std::cout << "Adaptee: Doing something with parameter " << param << "." << std::endl;
}
};
// Adapter 类
class Adapter : public Target {
private:
Adaptee* adaptee;
int param;
public:
Adapter(Adaptee* adaptee, int param) : adaptee(adaptee), param(param) {}
void request() const override {
adaptee->doSomething(param);
}
};
// 客户端代码
int main() {
Adaptee* adaptee = new Adaptee();
Target* adapter = new Adapter(adaptee, 42);
adapter->request(); // 调用适配器的方法
delete adaptee;
delete adapter;
return 0;
}
总结
这三种方式都可以在窄接口实现中使用,根据具体的需求选择不同的实现方式。抽象实现提供了灵活的适配能力,代理对象提供了额外的控制和功能,参数化适配器增强了适配器的灵活性。每种方式都有其适用的场景,选择合适的方式可以提高代码的可维护性和扩展性。