Java设计模式-适配器模式

适配器模式概述

与电源适配器相似,在适配器模式中引入了一个被称为适配器(Adapter)的包装类,而它所包装的对象称为适配者(Adaptee),即被适配的类。适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器让那些由于接口不兼容而不能交互的类可以一起工作。

适配器模式可以将一个类的接口和另一个类的接口匹配起来,而无须修改原来的适配者接口和抽象目标类接口。

适配器模式定义

将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

适配器模式结构图

适配器模式结构图

在对象适配器模式结构图中包含如下几个角色:

● Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

● Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

● Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

适配器模式示例

Sunny 软件公司 OA 系统需要提供一个加密模块,将用户机密信息(如口令、邮箱等)加密之后再存储在数据库中,系统已经定义好了数据库操作类。为了提高开发效率,现需要重用已有的加密算法,这些算法封装在一些由第三方提供的类中,有些甚至没有源代码。试使用适配器模式设计该加密模块,实现在不修改现有类的基础上重用第三方加密方法。

结构图设计如下:

适配器模式-InfomationEncryption

代码如下:

/**
 * Author: YiFan
 * Date: 2018/12/17 12:29
 * Description: 数据库操作接口
 */
public interface IDataBaseOperation {

    /**
     * 用户信息加密
     * @param info 用户口令、邮箱
     * @return 加密后的用户口令、邮箱
     */
    String informationEncryption(String info);
}

/**
 * Author: YiFan
 * Date: 2018/12/17 12:31
 * Description: 适配者类
 */
public class Adaptee {

    /**
     * 通过MD5算法加密用户信息
     * @param info 用户口令、邮箱
     * @return 加密的用户口令、邮箱
     */
    public String informationEncryptionByMD5(String info) {
        return MD5Util.MD5(info);
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/17 12:30
 * Description: 适配器类
 */
public class EncryptionAdapter implements IDataBaseOperation {

    private Adaptee adaptee;

    public EncryptionAdapter() {
        adaptee = new Adaptee();
    }

    @Override
    public String informationEncryption(String info) {
        return adaptee.informationEncryptionByMD5(info);
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/17 22:52
 * Description: MD5加密算法
 */
public class MD5Util {

    // MD5加密算法
    public static String MD5(String key) {
        char hexDigits[] = {
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };
        try {
            byte[] btInput = key.getBytes();
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/17 22:42
 * Description: 用户类
 */
public class User {

    // 用户昵称
    private String nickname;

    // 用户口令
    private String password;

    // 用户电话
    private String tel;

    // 用户邮箱
    private String email;

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/17 12:34
 * Description: 客户端测试类
 */
public class Client {

    public static void main(String[] args) {

        User user = new User();
        user.setPassword("123456789");
        user.setEmail("xyfaqq@163.com");

        // 针对接口编程
        IDataBaseOperation dataBaseOperation;
        // 实例化对象
        dataBaseOperation = new EncryptionAdapter();

        // 加密的用户密码
        String passwordEncryption = dataBaseOperation.informationEncryption(user.getPassword());

        // 加密的用户邮箱
        String emailEncryption = dataBaseOperation.informationEncryption(user.getEmail());

        System.out.println("加密前用户口令:" + user.getPassword() + ",加密后用户口令:" + passwordEncryption);

        System.out.println("加密前用户邮箱:" + user.getEmail() + ", 加密后用户邮箱:" + emailEncryption);
    }
}

直接结果为:

加密前用户口令:123456789,加密后用户口令:25F9E794323B453885F5181F1B624D0B
加密前用户邮箱:xyfaqq@163.com, 加密后用户邮箱:B187339EF36A225D8A161790844BC8A1

适配器模式总结

适配器模式将现有接口转化为客户类所期望的接口,实现了对现有类的复用,它是一种使用频率非常高的设计模式,在软件开发中得以广泛应用,在Spring等开源框架、驱动程序设计(如 JDBC 中的数据库驱动程序)中也使用了适配器模式。

主要优点

(1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。

(2) 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。

(3) 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

主要缺点

类适配器模式的缺点如下:

(1) 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;

(2) 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;

(3) 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

对象适配器模式的缺点如下:

与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

适用场景

(1) 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。

(2) 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

posted @ 2018-12-27 11:46  扇影无风  阅读(1371)  评论(0编辑  收藏  举报