• 设计模式 - 适配器模式


    实例

    数据加密

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


    现有实现

    • User.java
    /**
     * @Description 用户
     */
    public class User {
    
        private String token;
    
        private String mail;
    
        // 省略get、set、toString
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • DBUtil.java
    /**
     * @Description 数据库操作类
     */
    public class DBUtil {
    
        /**
         * 保存
         * @param user 用户
         */
        public void save(User user) {
            // 保存到数据库
            System.out.println("用户信息:" + user + " 保存到数据库");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • Encryption.java
    /**
     * @Description 加密类
     */
    public class Encryption {
    
        /**
         * 加密
         * @param str 加密字符
         * @return 加密后的字符
         */
        public String encrypt(String str) {
            // TODO 假设为加密方法
            return "***** " + str + " *****";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 输入如下:
    用户信息:User{token='123456789', mail='maggieq8324@gmail.com'} 保存到数据库
    
    • 1
    • 目前需要DBUtil和加密模块这两种不兼容的结构协同工作,在软件开发中,可以引入一个被称为适配器的角色来协调这些存在不兼容的结构,这种设计方案就是适配器模式

    适配器模式

    概念

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

    对象适配器模式

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

    在这里插入图片描述

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

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

    • DBOperation.java

    /**
     * @Description 数据库操作:抽象目标类接口
     */
    public interface DBOperation {
    
        /**
         * 保存
         * @param user 用户
         */
        void save(User user);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 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);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 输出如下:
    用户信息:User{token='***** 123456789 *****', mail='***** maggieq8324@gmail.com *****'}保存到数据库
    
    • 1
    • 类图如下:

    在这里插入图片描述

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

    类适配器模式

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

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

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

    • DBOperation.java

    /**
     * @Description 数据库操作:抽象目标类接口
     */
    public interface DBOperation {
    
        /**
         * 保存
         * @param user 用户
         */
        void save(User user);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 输出如下:
    用户信息:User{token='***** 123456789 *****', mail='***** maggieq8324@gmail.com *****'} 保存到数据库
    
    • 1
    • 类图如下:

    在这里插入图片描述

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

    缺省适配器模式

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

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

    • 在这里插入图片描述

    • DBOperation.java

    /**
     * @Description 数据库操作接口
     */
    public interface DBOperation {
    
        /**
         * 保存
         * @param user 用户
         */
        void save(User user);
    
        void save1(User user);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 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) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 测试代码同上

    • 输出如下:

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

    在这里插入图片描述

    相关思考

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

    总结

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

    源码


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

  • 相关阅读:
    el-table 指定层级展开
    图像特征(一)
    1.12 - 指令
    一种简单的数据库性能测试方法
    淘宝API接口,item_get-获得淘宝商品详情(淘宝商品详情 API 返回值说明)
    11-注意力机制
    CentOS7上docker部署prometheus+grafana
    你应该打好你的日志,起码避免被甩锅
    AI生成内容(AIGC)技术:革新创作与挑战未来
    缓存更新策略概览(Caching Strategies Overview)
  • 原文地址:https://blog.csdn.net/weixin_41182727/article/details/125984570