其他的设计模式也可以看看哈:
【高级感UP】掌握代理模式,编写优雅而高效的程序,C++实现代理模式揭秘,各种场景的示例代码-优快云博客
【设计模式】原型模式详解,带你走进C++实现的精彩世界-优快云博客
目录
前言:
我们继续来聊聊结构设计模式的其中一种:适配器模式;
一、原理及示例代码
适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。这种模式通常用于解决两个不兼容接口之间的适配问题。适配器模式包括类适配器模式和对象适配器模式两种实现方式。
类适配器模式
在类适配器模式中,适配器类继承目标接口,并且包含一个需要适配的类的实例。适配器类通过调用需要适配的类的方法来实现目标接口的方法。这种模式使用多重继承的方式,使得适配器类同时具有目标接口和需要适配类的功能。
对象适配器模式
在对象适配器模式中,适配器类包含一个需要适配的类的实例,并实现目标接口。适配器类通过调用需要适配的类的方法来实现目标接口的方法。这种模式使用组合的方式,使得适配器类可以与需要适配的类进行关联,实现目标接口的方法。
适配器模式的主要角色包括:
- 目标接口(Target):定义客户端使用的接口,客户端通过目标接口调用适配器的方法。
- 适配器(Adapter):实现目标接口,并包含一个需要适配的类的实例或继承需要适配的类,通过调用需要适配的类的方法来实现目标接口的方法。
- 需要适配的类(Adaptee):需要被适配的类,其接口与目标接口不兼容。
适配器模式的优点包括提高代码的重用性和可维护性,降低代码耦合度,以及可以适配多个类。然而,适配器模式也可能增加系统复杂性,影响性能,以及引入过多的适配器类。
适配器模式在实际应用中常用于将旧接口适配成新接口,或者将第三方库的接口适配成自己的接口。它能够很好地解决接口不兼容的问题,提高代码的重用性和可维护性。
以下是类适配器的C++示例代码:
#include <iostream>
#include <string>
// 目标接口
class Target {
public:
virtual void request() = 0;
};
// 需要适配的类
class Adaptee {
public:
void specificRequest() {
std::cout << "Adaptee's specific request" << std::endl;
}
};
// 类适配器
class Adapter : public Target, public Adaptee {
public:
void request() override {
specificRequest();
}
};
int main() {
Target *adapter = new Adapter();
adapter->request();
delete adapter;
return 0;
}
在这个示例中,Adapter
类继承了Target
和Adaptee
类,使用了多重继承来实现适配器模式。适配器类Adapter
直接使用了Adaptee
类的specificRequest()
方法来实现Target
接口的request()
方法。
以下是一个简单的C++示例代码,演示了对象适配器模式的使用:
#include <iostream>
#include <string>
// 目标接口
class Target {
public:
virtual void request() = 0;
};
// 需要适配的类
class Adaptee {
public:
void specificRequest() {
std::cout << "Adaptee's specific request" << std::endl;
}
};
// 对象适配器
class Adapter : public Target {
private:
Adaptee *adaptee;
public:
Adapter(Adaptee *a) : adaptee(a) {}
void request() override {
adaptee->specificRequest();
}
};
int main() {
Adaptee *adaptee = new Adaptee();
Target *adapter = new Adapter(adaptee);
adapter->request();
delete adaptee;
delete adapter;
return 0;
}
在这个示例中,Target
是需要的接口,Adaptee
是需要适配的类,Adapter
是适配器类。适配器类Adapter
使用了Adaptee
的对象组合来实现Target
接口。
二、结构图
适配器模式的结构图如下所示:
┌────────────┐ ┌──────────────┐
│ Client │──────>│ Target │
└────────────┘ └──────────────┘
▲
│
┌──────────────┐
│ Adaptee │
└──────────────┘
▲
│
┌────────────────┐
│ Adapter │
└────────────────┘
在这个结构图中,Client
需要使用Target
接口,但是Target
接口与Adaptee
类的接口不兼容。所以需要使用适配器模式,将Adaptee
类适配成符合Target
接口的类Adapter
。Adapter
类包含了Target
接口和Adaptee
类,并且使用Adaptee
类的方法来实现Target
接口。Client
使用Adapter
类来调用Target
接口。
三、使用场景:
适配器模式通常在以下情况下使用:
- 需要使用一个已有的类,但是它的接口与需要的接口不兼容。
- 需要使用多个类,但是它们的接口不兼容。
- 需要重用已有的类,但是它的接口不符合需要的接口。
适配器模式可以让这些不兼容的接口能够一起工作,从而提高代码的重用性和可维护性。在实际开发中,适配器模式经常用于将第三方库或遗留代码适配成符合自己需求的接口。
例如,一个应用程序需要使用一个第三方库的接口来处理图像,但是这个第三方库的接口与应用程序需要的接口不兼容。这时可以使用适配器模式,将第三方库的接口适配成符合应用程序需要的接口,从而让它们能够一起工作。
另外,适配器模式也可以用于系统的扩展,当需要添加新的功能时,可以通过适配器模式来扩展系统的功能,而不需要修改已有的代码。
当需要使用一个已有的类,但是它的接口与需要的接口不兼容时,可以使用适配器模式。以下是一个针对这种情况的C++示例代码:
#include <iostream>
#include <string>
// 目标接口
class Target {
public:
virtual void request() = 0;
};
// 需要适配的类
class Adaptee {
public:
void specificRequest() {
std::cout << "Adaptee's specific request" << std::endl;
}
};
// 类适配器
class Adapter : public Target, public Adaptee {
public:
void request() override {
specificRequest();
}
};
int main() {
Target *adapter = new Adapter();
adapter->request();
delete adapter;
return 0;
}
当需要使用多个类,但是它们的接口不兼容时,同样可以使用适配器模式。以下是一个针对这种情况的C++示例代码:
#include <iostream>
#include <string>
// 目标接口
class Target {
public:
virtual void request() = 0;
};
// 需要适配的类
class Adaptee1 {
public:
void specificRequest1() {
std::cout << "Adaptee1's specific request" << std::endl;
}
};
class Adaptee2 {
public:
void specificRequest2() {
std::cout << "Adaptee2's specific request" << std::endl;
}
};
// 类适配器
class Adapter : public Target, public Adaptee1, public Adaptee2 {
public:
void request() override {
specificRequest1();
specificRequest2();
}
};
int main() {
Target *adapter = new Adapter();
adapter->request();
delete adapter;
return 0;
}
当需要重用已有的类,但是它的接口不符合需要的接口时,同样可以使用适配器模式。以下是一个针对这种情况的C++示例代码:
#include <iostream>
#include <string>
// 目标接口
class Target {
public:
virtual void request() = 0;
};
// 需要适配的类
class Adaptee {
public:
void specificRequest() {
std::cout << "Adaptee's specific request" << std::endl;
}
};
// 类适配器
class Adapter : public Target {
private:
Adaptee *adaptee;
public:
Adapter(Adaptee *adaptee) : adaptee(adaptee) {}
void request() override {
adaptee->specificRequest();
}
};
int main() {
Adaptee *adaptee = new Adaptee();
Target *adapter = new Adapter(adaptee);
adapter->request();
delete adapter;
delete adaptee;
return 0;
}
这些示例代码展示了适配器模式在不同场景下的应用。
再来一个场景示例:
假设我们有一个旧的日志记录器类 OldLogger
,它有一个名为 log
的方法,而我们希望使用一个新的日志接口 Logger
,它有一个名为 writeLog
的方法。我们可以使用适配器模式来适配 OldLogger
到 Logger
接口。
#include <iostream>
#include <string>
// 旧的日志记录器类
class OldLogger {
public:
void log(const std::string& message) {
std::cout << "Old Logger: " << message << std::endl;
}
};
// 新的日志接口
class Logger {
public:
virtual void writeLog(const std::string& message) = 0;
};
// 适配器类,将 OldLogger 适配成 Logger 接口
class OldLoggerAdapter : public Logger {
private:
OldLogger* oldLogger;
public:
OldLoggerAdapter(OldLogger* logger) : oldLogger(logger) {}
void writeLog(const std::string& message) override {
oldLogger->log(message);
}
};
int main() {
// 使用适配器将 OldLogger 适配成 Logger 接口
OldLogger* oldLogger = new OldLogger();
Logger* logger = new OldLoggerAdapter(oldLogger);
// 调用新的 Logger 接口
logger->writeLog("This is a log message using the new Logger interface");
delete oldLogger;
delete logger;
return 0;
}
在这个示例中,我们使用了适配器模式将 OldLogger
适配成 Logger
接口。通过创建 OldLoggerAdapter
类,我们可以在不改变 OldLogger
的情况下,使其符合 Logger
接口的要求。
四、优缺点
适配器模式作为一种常见的设计模式,具有以下优点和缺点:
优点:
-
提高代码的重用性和可维护性:适配器模式可以将不兼容的接口转换成兼容的接口,从而提高代码的重用性和可维护性。
-
降低代码耦合度:适配器模式可以将客户端代码与需要适配的类解耦,使得它们之间的依赖关系更加灵活,减少了客户端对具体实现的依赖。
-
适配多个类:适配器模式可以同时适配多个类,使得系统更加灵活。
缺点:
-
增加系统复杂性:适配器模式会引入新的类和接口,从而增加系统的复杂性,使得代码结构变得更加复杂。
-
可能影响性能:适配器模式可能会引入额外的开销,例如适配器类的创建和调用可能会影响系统的性能。
-
可能引入过多的适配器类:在系统中过多地使用适配器模式可能会导致大量的适配器类,从而使得系统变得难以维护和理解。
-
潜在的设计问题:适配器模式有时可能会掩盖系统设计上的问题,例如不合理的接口设计或系统架构设计。
总的来说,适配器模式在某些情况下能够很好地解决接口不兼容的问题,提高代码的重用性和可维护性。但是在使用时需要权衡好优缺点,避免过度使用适配器模式造成系统复杂度的增加。适配器模式适合在需要对现有接口进行适配、需要复用现有功能并且接口不兼容的情况下使用。