定义
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
关于适配器模式的定义如上已经很清楚了,下面这幅图可以更清楚的帮助我们理解适配器模式。
适配器作为一个中间件,将两个不兼容的类链接起来。
根据适配器与被适配者类的关系不同,适配器模式可分为对象适配器和类适配器,在对象适配器模式中,适配器与适配者之间是关联关系,在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
对象适配器模式
UML
适配器模式主要有下面几个角色:
- 目标类(Target):目标类定义客户所需的接口,可以是一个接口、抽象类或者是一个具体类。
- 被适配者类(Adaptee):需要被适配的类,它有自己的方法,但不能被目标类直接调用,需要通过适配器进行适配。
- 适配器类(Adapter):通过适配器类对Target和Adaptee进行适配。
前面说过,对象适配器与类适配器之间的区别就是,适配器与被适配者之间的关系,这里对象适配器中,适配器与被适配者之间是关联关系。
适配器的主要代码如下:
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter() {
this.adaptee = new Adaptee();
}
@Override
public void request() {
adaptee.specificRequest();
}
}
类适配器模式
UML
可以看到这里与前面对象适配器的区别就是适配器与被适配者之间是继承(或实现)关系。
代码表示如下
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
由于在Java中是单继承的,所以这种类适配器模式的使用会受到限制,如果Target不是接口,而是具体类,就无法使用类适配器模式了,所以在Java中大部分情况下还是使用对象适配器更多一点。
实例
还是通过一个实例来理解吧:
软件公司OA系统需要提供一个加密模块,将用户机密信息(如口令、邮箱等)加密之后再存储在数据库中,系统已经定义好了数据库操作类。为了提高开发效率,现需要重用已有的加密算法,这些算法封装在一些由第三方提供的类中,有些甚至没有源代码。试使用适配器模式设计该加密模块,实现在不修改现有类的基础上重用第三方加密方法。
UML
根据需求我们可以规划结构图如下:
代码
主要代码如下
//加密的工具类
public class EncryptUtils {
public String MD5Encrypt(String str) {
String encode = null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
BASE64Encoder base64Encoder = new BASE64Encoder();
encode = base64Encoder.encode(md5.digest(str.getBytes()));
return encode;
} catch (NoSuchAlgorithmException e) {
System.out.println("加密出错");
return null;
}
}
public String DESEncrypt(String str) {
String password = "01234567";
String encode = null;
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
//创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
//Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
//用密匙初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
//现在,获取数据并加密
//正式执行加密操作
byte[] bytes = cipher.doFinal(str.getBytes());
BASE64Encoder base64Encoder = new BASE64Encoder();
encode = base64Encoder.encode(bytes);
return encode;
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public String RSAEncrypt(String str) {
return "实现RSA加密返回 ->_->";
}
}
//目标类
public abstract class EncryptModel {
abstract String userEncrypt(String str);
}
适配器类
public class EncryptAdapter extends EncryptModel {
private EncryptUtils encryptUtils;
public EncryptAdapter() {
this.encryptUtils = new EncryptUtils();
}
@Override
String userEncrypt(String str) {
return encryptUtils.MD5Encrypt(str);
}
}
客户端代码
public class Client {
public static void main(String[] args) {
EncryptModel encryptModel;
encryptModel = new EncryptAdapter();
String encode = encryptModel.userEncrypt("userName");
System.out.println("加密后的数据为:" + encode);
}
}
运行结果
加密后的数据为:Q14GSNY0F1xGvUCsNmVFqA==
成功通过适配器类使客户端调用加密工具类中的加密算法。
变种--缺省适配器
缺省适配器是适配器模式的一个边种,在Java中使用也挺广泛的。
定义
当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
UML
在Java开发中我们应该都遇到过这种情况,一个接口,定义了N种方法,当你使用的时候,又仅仅需要使用接口中的一两个方法,但你却必须实现这个接口中的所有方法,写了很长一段无意义的代码。缺省适配器就是为了解决这种情况,它通过定一个抽象类实现原始接口,实现接口中的所有方法,并给所有方法一个默认值或操作。当需要使用的时候,只需要定义一个类继承这个抽象类,重写你所需要的一两个方法即可。这个抽象类就是一个缺省适配器。
代码
原始接口
public interface InterfaceService {
void serviceOperation1();
String serviceOperation2();
int serviceOperation3();
void serviceOperation4();
}
缺省适配器
public abstract class ServiceAdapter implements InterfaceService {
@Override
public void serviceOperation1() {
}
@Override
public String serviceOperation2() {
return null;
}
@Override
public int serviceOperation3() {
return 0;
}
@Override
public void serviceOperation4() {
}
}
具体实现类
public class ConcreteService extends ServiceAdapter {
@Override
public void serviceOperation1() {
System.out.println("serviceOperation1");
}
}
小结
适配器模式应该是非常常见的一种设计模式了,作为Android开发工程师,我们最常见的应该就是各种适配器了,例如ListView和RecycleView的适配器。适配器模式将现有接口转换为客户端所期望的接口,实现了对现有类的复用。