无处不在的适配器模式

简介: 凡事都有例外,就是设计新系统的时候考虑使用第三方组件,因为没必要为了迎合第三方组件修改自己的软件设计风格,可以尝试使用适配器模式。

0x01:适配器模式简介


对于适配器相信不会陌生,生活中的例子比比皆是,像耳机转接线,充电器适配器,水管适配接口等等。通过类比很容易理解软件中的适配器模式。


客户端需要一个target(目标)接口,但是不能直接重用已经存在的adaptee(适配者)类,因为它的接口和target接口不一致,所以需要adapter(适配器)将adaptee转换为target接口。前提是target接口和已存在的适配者adaptee类所做的事情是相同或相似,只是接口不同且都不易修改。如果在设计之初,最好不要考虑这种设计模式。凡事都有例外,就是设计新系统的时候考虑使用第三方组件,因为没必要为了迎合第三方组件修改自己的软件设计风格,可以尝试使用适配器模式。


下面是一个非常典型的使用适配器模式的场景:


Sun公司在1996年公开了Java语言的数据库连接工具JDBC,JDBC使得Java语言程序能够与数据库连接,并使用SQL语言来查询和操作数据。JDBC给出一个客户端通用的抽象接口,每一个具体数据库厂商(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。抽象的JDBC接口和各个数据库引擎API之间都需要相应的适配器软件,这就是为各个不同数据库引擎准备的驱动程序。

另外一个比较典型的适配器场景J2EE规范与J2EE规范实现的服务器。SUN公司提供了一套J2EE规范,然后不同厂商根据自己的理解实现了不同的应用服务器。SUN公司提供了一套servlet api规范,然后实现这套规范的著名应用服务器有Apache Tomcat、Jetty、Oracle 的 Weblogic、IBM 的 WebSphere 等。


适配器模式的UML类图如下


微信图片_20220502085251.png


无处不在的适配器模式


从类图上看主要包含如下角色:


目标角色(target):这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口;

适配者角色(adaptee):已存在接口(可以理解是第三方提供的接口),但是和客户端期待的接口不兼容;

适配器角色(adapter):将已有接口转换成目标接口(可以理解我方需要的接口);


0x02:适配器模式的实现


  • 类适配器模式(class adapter pattern)


通过继承进行适配(类间继承)。类适配器模式在编译时实现target(目标)接口。这种适配器模式使用了多个实现了期待的接口或者已经存在的接口的多态接口。比较典型的就是:target接口被创建为一个纯粹的接口,Java不支持多继承的语言。


Target:Target目标角色,该角色定义把其他类转换为何种接口,也就是期望接口,通常情况下是一个接口或一个抽象类,一般不会是实现类


public interface Target {
public void request();
}


Adaptee:Adaptee源角色,想把谁转换为目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象


public class Adaptee {
public void specificRequest() {
        System.out.println("我是已经存在的运行良好的第三方厂商");
    }
}



Adapter:Adapter适配器角色,是适配器模式的核心角色,它的职责是通过继承或是类关联的方式把源角色转换为目标角色


public class Adapter extends Adaptee implements Target {
@Override
    public void request() {
super.specificRequest();
    }
}


ConcreteTarget:目标角色的实现类


public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("没有增加适配器的我方普通实现逻辑");
    }
}


类适配器模式测试代码


public class Client {
public static void main(String[] args) {
//原有无适配器的业务逻辑
Target target = new ConcreteTarget();
        target.request();
//增加适配器后的业务逻辑
Target target2 = new Adapter();
        target2.request();
    }
}


  • 对象适配器模式(object adapter pattern)


通过对象层次的关联关系进行委托(对象的合成关系/关联关系)。对象适配器模式在运行时实现target(目标)接口。在这种适配器模式中,适配器包装了一个类实例。在这种情况下,适配器调用包装对象实例的方法。


Target:客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口


public class Target {
public void request() {
        System.out.println("没有适配器的普通请求");
    }
}


Adaptee:需要适配的类


public class Adaptee {
public void specificRequest() {
        System.out.println("适配器类实现的特殊请求");
    }
}


Adapter:通过在内部包装一个Adaptee对象,把源接口转换成目标接口


public class Adapter extends Target {
private Adaptee adaptee = new Adaptee();
@Override
    public void request() {
//替换原理的逻辑,调用适配类的逻辑
        adaptee.specificRequest();
    }
}


对象适配器模式测试代码


public class Client {
public static void main(String[] args) {
Target target = new Adapter();
        target.request();
    }
}


  • 缺省适配器模式(default adapter pattern),也叫默认适配器模式、接口适配器模式


当不需要全部实现接口提供的方法时,可以设计一个适配器抽象类实现接口,并为接口中的每个方法提供默认方法实现或者空实现(如果大家做过GUI编程,就可以经常遇到这种实现,特别是各种控件的事件监听都提供了适配器类),抽象类的子类就可以有选择的覆盖父类的某些方法实现需求,它适用于一个接口不想使用所有的方法的情况。在java8后,接口中可以有default方法,就不需要这种缺省适配器模式了。接口中方法都设置为default,实现为空,这样同样同样可以达到缺省适配器模式同样的效果。


target:包含了很多没有实现的操作接口


public interface Target {
public abstract void operation1();
public abstract void operation2();
public abstract void operation3();
}


Adapter:默认实现了所有操作抽象类,只是所有的实现都是空实现


public abstract class DefaultAdapter implements Target{
@Override
    public void operation1() {
    }
@Override
    public void operation2() {
    }
@Override
    public void operation3() {
    }
}


测试缺省适配器模式需要用到的类(相当于GUI编程的一个组件,比如按钮Button)


public class Operator {
private Target target;
public void addOperation(Target target) {
this.target= target;
    }
public void operation1() {
        target.operation1();
    }
public void operation2() {
        target.operation2();
    }
public void operation3() {
        target.operation3();
    }
}


缺省适配器模式测试代码


public class Client{
public static void main(String[] args) {
// 原来要实现所有操作类的操作
Operator operator1= new Operator();
        operator1.addOperation(new Target() {
@Override
public void operation1() {}
@Override
public void operation2() {
System.out.println("invoke operation2");
            }
@Override
public void operation3() {}
        });
        operator1.operation2();
// 2、使用缺省适配器只需要实现需要用到的接口方法
Operator operator2 = new Operator();
        operator2.addOperation(new DefaultAdapter() {
@Override
public void operation2() {
System.out.println("invoke operation2");
            }
        });
        operator2.operation2();
    }
}


适配器模式本质上是现有的不兼容的接口转换为需要的接口。类适配器模式以继承现有类的方式转换;对象适配器模式以聚合对象实例的方式转换;接口适配器模式以实现接口的方式转换。适配器模式是在现有的类和系统都不易修改的情况下才使用,在系统设计之初慎用该设计模式。


相关文章
|
关系型数据库 MySQL
mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file
mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file
1596 2
|
关系型数据库 Java MySQL
从0开始部署阿里云服务器(萌新必看)
搭建阿里云服务器涉及购买、控制台介绍及配置。购买时可选择免费试用或直接购买,根据需求选择计费方式和地区。在服务器控制台,可找到实例并进行远程连接。配置包括Java环境,卸载OpenJDK,下载安装Java,配置环境变量。接着配置Tomcat,下载对应版本,解压并启动,确保防火墙允许8080端口。最后配置MySQL,安装,设置密码,更新远程访问权限。整个过程旨在提供一个基础的云服务器配置指南。
2889 1
|
机器学习/深度学习 人工智能 自然语言处理
智能客服的过去、现在和未来
随着IT技术的发展,客服行业迎来了变革的契机。
2410 0
智能客服的过去、现在和未来
|
17天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
9天前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
|
12天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
1051 34
|
11天前
|
机器学习/深度学习 人工智能 搜索推荐
万字长文深度解析最新Deep Research技术:前沿架构、核心技术与未来展望
近期发生了什么自 2025 年 2 月 OpenAI 正式发布Deep Research以来,深度研究/深度搜索(Deep Research / Deep Search)正在成为信息检索与知识工作的全新范式:系统以多步推理驱动大规模联网检索、跨源证据。
814 56