设计模式 - 适配器模式

实例

数据加密

假设一个系统需要使用加密模块将用户机密信息(如口令、邮箱等)加密之后再存储在数据库中的场景,系统已经定义好了数据库操作类,为了提高开发效率,需要重用已有的加密算法,这些算法封装在一些由第三方提供的类中,需求:实现在不修改现有类的基础上重用第三方加密方法


现有实现

  • User.java
/**
 * @Description 用户
 */
public class User {

    private String token;

    private String mail;

    // 省略get、set、toString
}
  • DBUtil.java
/**
 * @Description 数据库操作类
 */
public class DBUtil {

    /**
     * 保存
     * @param user 用户
     */
    public void save(User user) {
        // 保存到数据库
        System.out.println("用户信息:" + user + " 保存到数据库");
    }

}
  • Test.java
public class Test {
    public static void main(String[] args) {
        User user = new User();
        user.setToken("123456789");
        user.setMail("maggieq8324@gmail.com");

        DBUtil dbUtil = new DBUtil();
        dbUtil.save(user);
    }
}
  • Encryption.java
/**
 * @Description 加密类
 */
public class Encryption {

    /**
     * 加密
     * @param str 加密字符
     * @return 加密后的字符
     */
    public String encrypt(String str) {
        // TODO 假设为加密方法
        return "***** " + str + " *****";
    }

}
  • 输入如下:
用户信息:User{token='123456789', mail='maggieq8324@gmail.com'} 保存到数据库
  • 目前需要DBUtil和加密模块这两种不兼容的结构协同工作,在软件开发中,可以引入一个被称为适配器的角色来协调这些存在不兼容的结构,这种设计方案就是适配器模式

适配器模式

概念

  • 适配器模式(Adapter Pattern):将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作
  • 适配器模式用于解决不兼容结构问题
  • 适配器模式是一种结构型模式,可以作为类结构型模式,也可以作为对象结构型模式
  • 适配器模式可分为对象适配器模式和类适配器模式
  • 适配器模式角色定义:
角色 名称 释义
Target 目标抽象类 类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类
Adapter 适配器类 适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心
Adaptee 适配者类 被适配的角色

对象适配器模式

  • 对象适配器模式结构图(来自刘伟老师技术博客)

在这里插入图片描述

  • 在对象适配器模式中,适配器与适配者之间是关联关系

  • 对象适配器解决方案如下:

  • DBOperation.java

/**
 * @Description 数据库操作:抽象目标类接口
 */
public interface DBOperation {

    /**
     * 保存
     * @param user 用户
     */
    void save(User user);

}
  • OperationAdapter.java
/**
 * @Description 操作适配器:适配器
 */
public class OperationAdapter implements DBOperation {

    // 维持一个对适配者对象的引用
    private final Encryption encryption; // 适配者Encryption对象
    private final DBUtil dbUtil; // 适配者DBUtil对象

    public OperationAdapter() {
        encryption = new Encryption();
        dbUtil = new DBUtil();
    }

    @Override
    public void save(User user) {
        User encryptUser = new User();
        encryptUser.setToken(encryption.encrypt(user.getToken()));
        encryptUser.setMail(encryption.encrypt(user.getMail()));

        // 转发调用适配者类DBUtil的保存方法
        dbUtil.save(encryptUser);
    }
}
  • Test.java
/**
 * @Description 对象适配器测试类
 */
public class Test {

    public static void main(String[] args) {
        User user = new User();
        user.setToken("123456789");
        user.setMail("maggieq8324@gmail.com");

        DBOperation DBOperation = new OperationAdapter();
        DBOperation.save(user);
    }

}
  • 输出如下:
用户信息:User{token='***** 123456789 *****', mail='***** maggieq8324@gmail.com *****'}保存到数据库
  • 类图如下:

在这里插入图片描述

  • 如上所示,为了客户端能够使用加密与保存模块,提供了一个适配器类OperationAdapter,适配器类包装了两个适配者实例EncryptionDBUtil,从而将客户端与适配者衔接起来,在适配器的save方法中调用加密方法与数据库保存方法

类适配器模式

  • 类适配器模式结构图(来自刘伟老师技术博客)
    在这里插入图片描述

  • 在类适配器模式中,适配器与适配者之间是继承(或实现)关系

  • 类适配器解决方案如下:

  • DBOperation.java

/**
 * @Description 数据库操作:抽象目标类接口
 */
public interface DBOperation {

    /**
     * 保存
     * @param user 用户
     */
    void save(User user);

}
  • OperationAdapter.java
/**
 * @Description 操作适配器:适配器
 */
public class OperationAdapter extends Encryption implements DBOperation {

    private final DBUtil dbUtil; // 适配者DBUtil对象

    public OperationAdapter() {
        this.dbUtil = new DBUtil();
    }

    @Override
    public void save(User user) {
        User encryptUser = new User();
        encryptUser.setToken(super.encrypt(user.getToken()));
        encryptUser.setMail(super.encrypt(user.getMail()));

        // 转发调用适配者类DBUtil的保存方法
        dbUtil.save(encryptUser);
    }
}
  • Test.java
/**
 * @Description 类适配器测试类
 */
public class Test {
    public static void main(String[] args) {
        User user = new User();
        user.setToken("123456789");
        user.setMail("maggieq8324@gmail.com");

        DBOperation DBOperation = new OperationAdapter();
        DBOperation.save(user);
    }
}
  • 输出如下:
用户信息:User{token='***** 123456789 *****', mail='***** maggieq8324@gmail.com *****'} 保存到数据库
  • 类图如下:

在这里插入图片描述

  • 如上所示,适配器类实现了抽象目标类接口DBOperation,并继承了适配者类,在适配器类的save方法中调用所继承的适配者的加密方法实现了适配

缺省适配器模式

  • 缺省适配器模式是适配器模式的一种变体,当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式

  • 缺省适配器模式结构图(来自刘伟老师技术博客)

  • 在这里插入图片描述

  • DBOperation.java

/**
 * @Description 数据库操作接口
 */
public interface DBOperation {

    /**
     * 保存
     * @param user 用户
     */
    void save(User user);

    void save1(User user);

}
  • AbstractOperation.java
/**
 * @Description 数据库操作接口抽象类
 */
public abstract class AbstractOperation implements DBOperation {

    private final DBUtil dbUtil;

    protected AbstractOperation() {
        this.dbUtil = new DBUtil();
    }

    @Override
    public void save(User user) {
        dbUtil.save(user);
    }

    @Override
    public void save1(User user) {

    }
}
  • OperationAdapter.java
/**
 * @Description 操作适配器:适配器
 */
public class OperationAdapter extends AbstractOperation {

    private final Encryption encryption; // 适配者Encryption对象

    public OperationAdapter() {
        this.encryption = new Encryption();
    }

    @Override
    public void save(User user) {
        User encryptUser = new User();
        encryptUser.setToken(encryption.encrypt(user.getToken()));
        encryptUser.setMail(encryption.encrypt(user.getMail()));

        // 调用父类的实现
        super.save(encryptUser);
    }
}
  • 测试代码同上

  • 输出如下:

用户信息:User{token='***** 123456789 *****', mail='***** maggieq8324@gmail.com *****'}保存到数据库
  • 类图如下:

在这里插入图片描述

相关思考

  • 在对象适配器模式中,一个适配器能否适配多个适配者?
能,关联关系能添加多个
  • 在类适配器模式中,一个适配器能否适配多个适配者?
不能,Java不支持多继承

总结

  • 优点
1.能提高类的透明性和复用,现有的类复用但不需要改变
2.目标类和适配器类解耦,提高程序扩展性
3.符合开闭原则
  • 缺点
1.适配器编写过程需要全面考虑,可能会增加系统的复杂性
2.增加系统代码可读难度
  • 适用场景
1.已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)
2.不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案
  • 适配器模式源代码
XmlAdapter(JAXB)、AdvisorAdapter(Spring)、JpaVendorAdapter(JPA)、HandlerAdapter(SpringMVC)

源码


- End -
- 个人学习笔记 -
- 仅供参考 -

posted @ 2022-07-31 22:05  Maggieq8324  阅读(69)  评论(0编辑  收藏  举报