在软件设计中,适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的类能够合作工作。适配器模式通过将一个类的接口转换成客户端所期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。适配器模式通常分为对象适配器和类适配器,这里我们主要讨论对象适配器。
介绍
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
适配器模式适合应用场景
当你希望使用某个类, 但是其接口与其他代码不兼容时, 可以使用适配器类。
适配器模式允许你创建一个中间层类, 其可作为代码与遗留类、 第三方类或提供怪异接口的类之间的转换器。
如果您需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。
你可以扩展每个子类, 将缺少的功能添加到新的子类中。 但是, 你必须在所有新子类中重复添加这些代码, 这样会使得代码有重复。
将缺失功能添加到一个适配器类中是一种优雅得多的解决方案。 然后你可以将缺少功能的对象封装在适配器中, 从而动态地获取所需功能。 如要这一点正常运作, 目标类必须要有通用接口, 适配器的成员变量应当遵循该通用接口。 这种方式同装饰模式非常相似。
适配器模式的主要角色
- 目标(Target):定义客户所需的接口。
- 适配者(Adaptee):需要适配的现有接口。
- 适配器(Adapter):将适配者接口转换为目标接口。
适配器模式的实现步骤
- 定义目标接口。
- 创建需要适配的类。
- 创建适配器类,实现目标接口,并将需要适配的类组合进来。
C++中的适配器模式实现
假设我们有一个旧的打印系统(OldPrinter
),它只有一个打印方法,但是新系统需要一个具有更多功能的打印机接口(Printer
)。我们通过适配器模式来实现这一转换。
1. 定义目标接口(Target)
// Target interface
class Printer {
public:
virtual void print() = 0;
virtual void printInColor() = 0;
virtual ~Printer() = default;
};
2. 创建需要适配的类(Adaptee)
// Adaptee
class OldPrinter {
public:
void simplePrint() {
std::cout << "Printing in black and white." << std::endl;
}
};
3. 创建适配器类(Adapter)
// Adapter
class PrinterAdapter : public Printer {
private:
OldPrinter* oldPrinter;
public:
PrinterAdapter(OldPrinter* printer) : oldPrinter(printer) {}
void print() override {
oldPrinter->simplePrint();
}
void printInColor() override {
// 对于旧打印机没有彩色打印功能,我们可以选择不实现或者模拟一种行为
std::cout << "Simulated color printing." << std::endl;
}
};
4. 客户端代码
int main() {
OldPrinter* oldPrinter = new OldPrinter();
Printer* printer = new PrinterAdapter(oldPrinter);
printer->print(); // 使用适配器调用旧打印机的黑白打印功能
printer->printInColor(); // 使用适配器调用模拟的彩色打印功能
delete printer;
delete oldPrinter;
return 0;
}
适配器模式的优缺点
优点:
- 可以将现有的类与未兼容的接口进行集成。
- 可以增强现有类的功能,而无需修改其代码。
- 符合开闭原则(对扩展开放,对修改关闭)。
缺点:
- 适配器模式会增加系统的复杂性。
- 在某些情况下,代码可能会变得更加难以理解。
通过适配器模式,我们可以轻松地将旧系统的功能集成到新系统中,而无需修改旧系统的代码,从而实现代码复用和扩展。这种模式在实际开发中非常实用,尤其是在处理遗留系统时。