自大学课程初识设计模式以来,就越发觉得有必要系统学习一下设计模式。
刚好在实习前准备期间课比较少,抽出一点时间整理一下记一些笔记,复制粘贴比较多。
笔记比较适合学习过设计模式的同学。
Adapter Pattern(适配器模式)
学习链接:极客学院Wiki_Java设计模式之结构型模式
另外感谢刘伟博士,学习设计模式可以看刘伟博士的博客,很详尽。
刘伟技术博客
适配器模式的适用范围
(1) 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
(2) 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
没懂。。。。。
应用实例
在 JDK 类库的事件处理包 java.awt.event 中广泛使用了缺省适配器模式,如 WindowAdapter、KeyAdapter、MouseAdapter 等。在 Spring 等开源框架、驱动程序设计(如 JDBC 中的数据库驱动程序)中也使用了适配器模式。
适配器模式如何实现
角色
Target(目标抽象类)
目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类)
适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类)
适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
类图
这个类图是对象适配器的
另外还有是类适配器,但是一般不常用,因为更不实用,使用组合比使用继承要更加灵活(在桥接模式中体现明显)。
类适配器就是改变了适配器和适配者的联系,使适配器继承了适配者。这样子一个适配器就只能对应一个适配者了。但是好处就是适配器可以重写适配者的某些方法。
扩展适配器
最基础的适配器是上述的对象适配器,但在实际应用中会显得不那么灵活和功能单一,于是引出下面两种适配器的扩展:
双向适配器
在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器
缺省适配器
当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
缺省适配器的出现就是简化了我们写接口的空实现方法,缺点就是缺省适配器是抽象类,用起来必须要继承。
适配器模式的优缺点
主要优点
(1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
(2) 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
(3) 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
(4) (对象适配器专属)一个对象适配器可以把多个不同的适配者适配到同一个目标;
(5) (对象适配器专属)可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。
主要缺点
(1) 对于 Java、C# 等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
(2) 适配者类不能为最终类,如在 Java 中不能为 final 类,C# 中不能为 sealed 类;
(3) 在 Java、C# 等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
练习
题目
Sunny 软件公司 OA 系统需要提供一个加密模块,将用户机密信息(如口令、邮箱等)加密之后再存储在数据库中,系统已经定义好了数据库操作类。为了提高开发效率,现需要重用已有的加密算法,这些算法封装在一些由第三方提供的类中,有些甚至没有源代码。试使用适配器模式设计该加密模块,实现在不修改现有类的基础上重用第三方加密方法。
题目分析
我们先来分析下这个题目,题目提到:加密模块、数据库操作类、加密算法、第三方提供的类,这几个名词,因此分析下来看,数据库操作类是目标抽象类,加密模块是该类的抽象方法,第三方提供的类是适配者,加密算法是需要用的方法,明确了各个角色以后开始编写代码。
实现代码
目标抽象类:DataBaseService.java
package com.joy;
public abstract class DataBaseService {
public abstract void encrypt(String password);
}
适配者:JavaAPI.java
package com.joy;
public class JavaAPI {
//无法改动的核心加密算法
public String encryptAPI(String needEncrypt){
StringBuilder sb = new StringBuilder();
for(int i=0;i<needEncrypt.length();i++)
{
char ch = needEncrypt.toCharArray()[i];
//每一位的ASCII码都加10
ch+=10;
sb.append(ch);
}
return sb.toString();
}
}
适配器:Adapter.java
package com.joy;
public class Adapter extends DataBaseService{
@Override
public void encrypt(String password) {
System.out.println("原密码是: "+password);
String encryption = new JavaAPI().encryptAPI(password);
System.out.println("加密后密码: "+encryption);
}
}
客户端:Clien.java
package com.joy;
public class Clien {
public static void main(String[] args) {
DataBaseService dbs = new Adapter();
dbs.encrypt("password");
}
}
运行结果
总结
适配器感觉还是蛮简单的,用途也是很广泛,需要弄清各种角色,并且也需要适配者和目标具有一定的特性,比如可以继承啊,可以使实现啊。