简介
IT 世界有句俗语:没有什么问题是加一层不能解决的!如果有,那就再加一层!适配器模式还有个别名叫:Wrapper(包装器),顾名思义就是将目标类用一个新类包装一下,相当于在客户端与目标类又直接加了一层。
官方一点的说法就是:将一个接口转换为客户端所期待的接口,从而使两个接口不兼容的类可以在一起工作。
我们代入生活场景来看的话:
- 电压转换器:不同国家的电压规格各异,同样功率的电器在不同的地方工作时需要不同的电压,电压转换器作为适配器,将不同电压转换成电器使用标准电压。
- 耳机转接头:有些手机没有耳机插口,需要使用转接头适配器,将耳机转换为手机支持的接口,实现对不同的耳机兼容。
使用场景
示例
Java 中用到适配器模式的例子:
JDBC驱动程序:不同的数据库提供商实现了不同的JDBC驱动接口,使用适配器模式可以将这些不同的接口适配为标准的JDBC接口,提高应用程序的可移植性。
日志框架:Java中有多个常用的日志框架,如Log4j、SLF4J等,不同的日志框架提供的API不同,使用适配器模式可以将这些不同的API适配为一个统一的接口,方便再程序中进行日志记录和管理。
第三方库或SDK:在使用第三方库或 SDK 时,可能由于它们实现的 API 不同而导致应用程序复杂,使用适配器模式可以将不同的 API 适配为统一的接口,简化应用程序的调用。
UML 类图
从上图可见,适配器模式只有3个角色。
- Target
是一个接口,它是我们客户端使用的目标接口。
- Adaptee
我们想要使用的接口与Target
不兼容的类,它可以是一个接口,也可以是一个类。
- Adapter
适配器类,此模式的核心。它需要实现目标接口Target
,而且必须要引用Adaptee
,因为我们要在此类中包装Adaptee
的功能。
需求:
比如我们要把系统原来的日志接口,利用三方库提供的接口转换为系统的目标接口,通过适配器客户端就切换到新的第三方日志系统。
系统原来的日志接口:
public interface LogFactory {
void debug(String tag, String message);
}
第三方库提供的日志功能,但是与原系统使用的不兼容:
public interface NLogger {
void d(int priority, String message, Object ... obj);
}
public class NLoggerImpl implements NLogger {
@Override
public void d(int priority, String message, Object... obj) {
System.out.println(String.format("第三方logger记录:%s", message));
}
}
适配器模式的核心,通过此类就可以将三方库提供的接口转换为系统的目标接口:
public class LogAdapter implements LogFactory {
private NLogger nLogger;
public LogAdapter(NLogger nLogger) {
this.nLogger = nLogger;
}
@Override
public void debug(String tag, String message) {
Objects.requireNonNull(nLogger);
nLogger.d(1,message);
}
}
LogAdapter
实现了系统的目标接口,同时持有三方库NLogger
的引用。
客户端使用:
public class AdapterClient {
public void recordLog() {
LogFactory logFactory = new LogAdapter(new NLoggerImpl());
logFactory.debug("Test", "我将使用第三方logger打印log");
}
}
我们可以发现,正常的调用我们原日志系统的api,但是通过适配器类真正的调用了第三方类。
总结
技术要点:适配器LogAdapter
必须要实现目标接口,且依赖那个提供功能的类型,此处为NLogger。
优缺点:
优点:极大的增强了程序的可扩展性,通过此模式,你可以随意扩展程序的功能,但却不需要修改接口。
缺点:唯一可以称的上的缺点是多了一层,但是这也是没有办法的事情。这也算不上缺点吧。