设计模式--适配器模式的思考
个人认为适配器模式是一种加中间层来解决问题的思想,为的是减少开发工作量,提高代码复用率.另外在对于第三方的服务中使用适配器层则可以很好的把自己系统与第三方依赖解耦,降低依赖.
什么是适配器模式
适配器模式: 将一个类的接口转换为客户所期望的另一个接口.适配器让原本接口不兼容的类可以合作无间.类图如下:
Client: 调用方
Target: 需要提供的新功能
AdaptedObject: 系统中原本存在的类似本次需要提供的新功能的类
Adapter: Target的实现类,主要负责该功能的实现,其内部持有AdaptedObject
的对象,利用其对象完成本次需要提供的新功能.
整个流程大概如下:
1.客户通过目标接口调用适配器的方法发出请求.
2.适配器(Adapter)使用被适配器(AdaptedObject)已有的功能完成客户所期望的新功能
3.客户收到调用结果,但是并不知道是适配器起到的转换作用.
那么Adapter
利用已经完成的AdaptedObject
类实现本次提供的新功能,这一过程就是适配.
Java I/O中的适配器
在Java I/O中有把字节流转换为字符流的类java.io.InputStreamReader
以及java.io.OutputStreamWriter
.那么这两个类实际上使用的就是适配器模式
以InputStreamReader
为例,其继承了Reader
类,所提供的功能是把字节流转换为字符流,其内部拥有StreamDecoder
这一实例,所有的转换工作是由该实例完成.
public int read(char cbuf[], int offset, int length) throws IOException {
// 使用被适配器的功能
return sd.read(cbuf, offset, length);
}
那么在这个例子中
Client是调用方,也就是我们开发人员
Target是Reader
这个抽象类.
AdaptedObject是StreamDecoder
,利用的是其功能.
Adapter是InputStreamReader
Java Set集合中的适配器
Java中的Set集合有者无序,唯一元素,查找复杂度O(1)等特性.这些特性Map数据结构的key是完全符合的,那么就可以利用适配器模式来完成Set的功能.
以HashSet
为例,其内部持有的是一个值为固定Object的Map,如下图
其所有的操作会通过HashSet
这个适配器来操作HashMap
这个被适配器.比如:
public Iterator<E> iterator() {
return map.keySet().iterator();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Client是调用方,也就是我们开发人员
Target是Set
这个接口.
AdaptedObject是HashMap
,利用的是其功能.
Adapter是HashSet
Mybatis中的适配器模式
Mybatis作为一款通用框架,对于日志处理必然需要适配到各种日志框架,比如slf4j
,log4j
,logback
等,每个日志的API多多少少有点不同,这种情况下适配器模式就起到了转换的作用.
以下图由于实现类太多,只列取了几个.
Mybatis有自己的org.apache.ibatis.logging.Log
接口,框架内部使用的都是自己的Log,具体使用哪一个Log是由配置中的适配器决定的.
以org.apache.ibatis.logging.log4j2.Log4j2LoggerImpl
适配器为例,org.apache.logging.log4j.Logger
为被适配者.Log4j2LoggerImpl
是适配器,起到了转换的作用.
public class Log4j2LoggerImpl implements Log {
private static final Marker MARKER = MarkerManager.getMarker(LogFactory.MARKER);
//被适配者
private final org.apache.logging.log4j.Logger log;
public Log4j2LoggerImpl(Logger logger) {
log = logger;
}
@Override
public boolean isDebugEnabled() {
return log.isDebugEnabled();
}
.....
}
与装饰者模式的区别
个人认为这两种设计模式是完全不同的思想:
装饰者模式本意是增强功能,其装饰者与被装饰者对于调用方是很清晰的,比如ContreteDecoratorA decoratorA = new ContreteDecoratorA(new ComponentInterfaceImpl());
就很清晰的知道使用ContreteDecoratorA
装饰了ComponentInterfaceImpl
.另外ContreteDecoratorA
并没有改变ComponentInterfaceImpl
的功能提供出去,而是为其进行了增强处理.
适配器模式本意是复用已有的代码,对已经存在的功能进行包装转换,以另一种形式提供出去.比如HashSet
,对于调用方来说其内部使用的HashMap
是不可见的,调用方不关心内部被适配者是谁,只是关注该功能本身也就是Set
接口.
要说相同点的话那就是都是组合复用思想对一个对象进行包装,但其目的有着本质的区别.还望好好理解.
与外观模式的区别
外观模式本意是把一组复杂的关联行为进行包装,提供一个面向开发人员更为简单的使用方式.举个例子,你觉得JDBC方式不太好用,因此写了个DBUtils这种封装类,实际上就是一种外观模式,与适配器还是有着很大的区别.
备注
一家之言,个人主观理解还是有很多的,如果有错误还请指出.
- 使命必达: 深入剖析WCF的可靠会话[协议篇](下)
- 分页控件和几个相关控件的源代码
- Python编程中的反模式
- Python机器学习库:Scikit-Learn简介
- 很简单的企业管理器---我写程序的方式,几个自定义控件。
- 使命必达: 深入剖析WCF的可靠会话[原理揭秘篇](上)
- 其实添加数据也可以这样简单——表单的第三步抽象(针对UI及后置代码)
- 为Symfony2和Redis正名,基于PHP的10亿请求/周网站打造
- 如何使用Python基线预测进行时间序列预测
- 如何使用统计显着性检验来解释机器学习结果
- 其实添加数据也可以这样简单——表单的第一步抽象(针对数据访问层)《怪怪设计论: 抽象无处不在 》有感
- WCF服务端运行时架构体系详解[上篇]
- 使命必达: 深入剖析WCF的可靠会话[编程篇](下)
- 在网页里让文本框只能输入数字的一种方法。外加回车换Tab
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 逻辑式编程还有用吗?--“三维度”逻辑编程语言的设计(2)
- git 报错解决Validate branches Cannot Create: This merge request already exists
- 树莓派基础实验20:火焰报警传感器实验
- (译)SDL编程入门(8)几何图形渲染
- Java8 dubbo 调用 Collectors.toMap代码片发生的异常(IllegalStateException: Duplicate key)
- 树莓派基础实验21:烟雾报警传感器实验
- 树莓派基础实验22:红外遥控传感器实验
- Spring的BeanUtil的copyProperties方法 慎用!!
- (译)SDL编程入门(9)视口
- (译)SDL编程入门(7)纹理加载和渲染
- 三步带你开发一个短链接生成平台
- 绕安全狗的那些事
- single-spa 基础概念
- DVWA-对Command Injection(命令注入)的简单演示与分析
- 如何探测内网存活主机