前言
从这篇文章开始来盘一盘结构型设计模式,在开始之前先来简单回顾一下创建型的设计模式有哪些,如果有兴趣,就来一起学习吧:
掌握简单工厂模式:高效开发的必备利器 - 掘金 (juejin.cn)
工厂方法模式:改变你对软件开发的认知 - 掘金 (juejin.cn)
抽象工厂模式:角色解析与应用探索 - 掘金 (juejin.cn)
设计模式之单例模式:不同实现方式的深度解析 - 掘金 (juejin.cn)
解密原型模式:工作原理与实际应用 - 掘金 (juejin.cn)
掌握建造者模式:构建复杂对象的灵活解决方案 - 掘金 (juejin.cn)
那么结构型的设计模式有哪些呢?结构型模式是用于处理类或对象的组合的设计模式,共七种,包括适配器模式、装饰者模式、代理模式、门面模式、桥梁模式、组合模式、享元模式。这篇文章的主角就是适配器模式。
什么是适配器模式
适配器模式(Adapter Pattern)允许将一个类的接口适配成用户所期待的接口,从而使原本因接口不兼容而不能在一起工作的类能够一起工作。
适配器模式分为两种类型:对象适配器模式和类适配器模式。
- 对象适配器模式:在这种适配器模式中,适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。
- 类适配器模式:这种适配器模式下,适配器继承自已实现的类。
适配器模式的核心原理
适配器模式的核心是将一个类的接口转换成客户端所期望的另一种接口,从而使原本因接口不匹配而导致无法在一起工作的两个类能够一起工作。适配器模式是一种结构型设计模式,它通过引入一个适配器类来将不兼容的接口转换为兼容的接口。
适配器模式中有三个关键角色,分别是目标角色(Target)、源角色(Adaptee)和适配器角色(Adapter)。
- 目标(Target):目标接口,也就是我们期待的接口,客户端使用这个接口来调用适配器。
- 源(Adaptee):这是需要被适配的接口,通常也是需要被转换的接口。
- 适配器(Adapter):这是将源接口适配成目标接口的类。
适配器模式的核心是将两个没有关系的类组合在一起使用,只要传递了某个类的对象,就可以在适配器中调用这个类的方法。在实现适配器模式时,通常需要编写一个适配器类来实现目标接口,并将源接口转换为目标接口的实现方法。同时,还需要在客户端中使用目标接口来调用适配器的实例,从而间接地调用源接口的方法。
1、类的适配器模式:通过继承源接口类来实现适配,需要修改源接口类的继承结构。
例如,将一个接口适配成另一个接口:
java
interface Target {
void request();
}
class Adaptee {
void specificRequest() {
System.out.println("Adaptee's specific request.");
}
}
class Adapter extends Adaptee implements Target {
public void request() {
specificRequest();
}
}
在这个例子中,适配器Adapter继承了Adaptee类,实现了Target接口。适配器将Adaptee的特定请求(specificRequest)适配成Target接口的request方法。客户端通过Target接口来调用适配器,从而间接地调用Adaptee的特定请求。
2、对象适配器模式:通过将源对象封装在适配器对象中来实现适配,不需要修改源接口类的继承结构。
例如,将一个类的对象适配成另一个类的对象:
java
interface Target {
void request();
}
class Adaptee {
void specificRequest() {
System.out.println("Adaptee's specific request.");
}
}
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
adaptee.specificRequest();
}
}
在这个例子中,适配器Adapter实现了Target接口。适配器将Adaptee的对象封装在适配器对象中,适配器的request方法通过调用Adaptee的特定请求(specificRequest)来实现适配。客户端通过Target接口来调用适配器,从而间接地调用Adaptee的特定请求。
适配器模式要解决什么问题
在计算机编程中,适配器模式可以用于解决接口不兼容的问题,它通过将一个类的接口包裹在一个已存在的类中,从而将该类的接口转换成客户所期待的接口。适配器的宗旨是保留现有类所提供的服务,向客户提供接口,以满足客户的期望。
在Java中,适配器模式如何实现
在生活当中,适配器的例子比比皆是,如在中国,生活用电的标准是220V的交流电,但是平常使用的手机的标准电压是5V的直流电,充电线末端的充电头实际上就是一个适配器,主要功能就是把220V的交流电转换成了手机需要的5V直流电。
java
/**
* 5v直流电抽象接口类
*/
public interface DC5 {
Integer outputDC5v();
}
java
/**
* 220v交流电实体类
*/
public class AC220 {
public Integer outputAc220v() {
Integer output = 220;
System.out.println("输出交流电:" + output + "V");
return output;
}
}
类适配器
java
/**
* 220v转5v适配器类
*/
public class PowerAdapter extends AC220 implements DC5{
@Override
public Integer outputDC5v() {
Integer outputAc220v = this.outputAc220v();
System.out.println("适配器开始工作");
int output5v = outputAc220v / 44;
System.out.println("适配器输出电压:"+output5v+"5");
return output5v;
}
}
对象适配器
java
/**
* 220v转5v适配器类
*/
public class PowerAdapter2 implements DC5{
private AC220 ac220;
public PowerAdapter2(AC220 ac220) {
this.ac220 = ac220;
}
@Override
public Integer outputDC5v() {
Integer outputAc220v = ac220.outputAc220v();
System.out.println("适配器开始工作");
int output5v = outputAc220v / 44;
System.out.println("适配器输出电压:"+output5v+"5");
return output5v;
}
}
java
/**
* 适配器模式
*/
public class Test {
public static void main(String[] args) {
//类适配器
System.out.println("类适配器:--->");
DC5 dc5=new PowerAdapter();
dc5.outputDC5v();
//对象适配器
System.out.println("对象适配器:--->");
AC220 ac220 = new AC220();
DC5 dc52=new PowerAdapter2(ac220);
dc52.outputDC5v();
}
}
适配器模式有哪些应用场景
如果你在业务的开发过程中,遇到以下这些场景,就可以选择使用适配器模式,来使你的代码实现更优雅和高效:
- 系统需要使用现有的类,而此类的接口不符合需要。
- 需要一个统一的输出接口,而输入类型不可预知(如Android中的Adapter)。
- 创建一个可以复用的类(如Android中的Adapter),使得该类可以与其他不相关的类或不可预见的类协同工作。
- 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。
适配器模式在Spring中有哪些应用
适配器模式在Spring框架中有多处应用:
- 在Spring的AOP模块中,适配器模式被用于增强被代理类的功能。例如,Spring的AOP模块提供了通知(Advice)来增强被代理类的功能,这些通知有BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等类型。每种类型的Advice都有对应的拦截器,例如MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。这些不同的拦截器通过适配器统一对外提供接口。
- 在Spring中,适配器模式也常被用于处理不同类型的"DisposableBean"。例如,当Spring容器启动时,会将所有"DisposableBean"添加到disposableBeans集合中。这些"DisposableBean"可能包括通过@Bean注入的实现了close方法的UserService、实现了DisposableBean接口的重写了destory()方法的类、实现了AutoCloseable接口的类等。在关闭容器销毁单例Bean时,Spring会找出实现了"DisposableBean"并执行对应的方法进行Bean的销毁。这就需要一个适配器,比如DisposableBeanAdapter,将这些判断封装起来,外部使用者直接调用DisposableBeanAdapter的destory()方法即可。
总结
优点:
- 提高类的透明性和复用,现有的类复用但不需要改变。
- 目标类和适配器类解耦,提高程序的扩展性。
- 在很多业务场景中符合开闭原则。
缺点:
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。