定义
将一个类的接口(被适配器的类)转换成客户期望的另一个接口(目标类),使原本接口不兼容的类可以一起工作。
主要分为两种
- 对象适配器:通过组合实现
- 类适配器:通过继承实现
适用场景
- 已经存在的类,它的方法和需求不匹配时(方法结果相似或相同)
- 不是软件设计阶段考虑的设计模式,而是软件维护过程中,由于不同产品,不同厂家造成功能类似而接口不同的情况下的解决方案。
优点
- 提高类的透明性和复用性。
- 目标类和适配器类解耦。
- 符合开闭原则。
缺点
- 增加系统复杂性
- 增加代码阅读的难度
适配器模式模型
类适配器模式
类适配器模式,说通俗来说,就是适配器继承被适配类并且实现目标类所需要的接口。下面我们来简单的实现以下这个模式。
被适配的类
/**
* 被适配的类
*/
public class AdaptedClass {
public void AdaptedOwnMethod() {
System.out.println("被适配者的方法");
}
}
适配器目标类接口
/**
* 适配器目标类接口
*/
public interface AdaptTarget {
void request();
}
目标类的原始实现
/**
* 目标类的原始实现
*/
public class TargetClass implements AdaptTarget {
@Override
public void request() {
System.out.println("目标的原方法");
}
}
适配器
/**
* 适配器
*/
public class Adapter extends AdaptedClass implements AdaptTarget{
public Adapter() {
System.out.println("适配开始");
}
@Override
public void request() {
System.out.println("适配成功");
System.out.println("目标类输出被适配器的方法结果--");
super.AdaptedOwnMethod();
}
}
UML
我们可以看到 适配器即继承了被适配的类,又实现了适配目标类的接口。因此我们可以通过他,来改变目标类原有逻辑。
在这个模型里,我们看到一个目标类的具体实现(TargetClass
) ,当有业务改变时,该类可能已经不满足需求了,因此我们需要 适配器Adapter
来改变目标类的实现。
适配测试类
public class Test {
public static void main(String[] args) {
//目标原方法
AdaptTarget target = new TargetClass();
target.request();
System.out.println("-------业务调整 需要适配其他方法----------");
//使用适配器,获取适配的目标类方法
AdaptTarget adaptTarget = new Adapter();
adaptTarget.request();
}
}
结果
对象适配器
在这里 我们的被适配者(AdaptTarget
)不变,适配器目标类接口(AdaptTarget
)不变,目标原实现类(目标类的原始实现
)不变。只需要改变适配器Adapter
:
public class Adapter implements AdaptTarget {
//组合被适配的对象
AdaptedClass adaptedClass = new AdaptedClass();
public Adapter() {
System.out.println("适配开始");
System.out.println("组合 被适配对象 -- AdaptedClass");
}
@Override
public void request() {
System.out.println("适配成功");
System.out.println("目标类输出被适配器的方法结果--");
adaptedClass.AdaptedOwnMethod();
}
}
我们可以看到,适配器不再继承被适配者的类,而是使用组合模式,将被适配者组合进了适配器中。
UML类图
结果
简单的业务示例
我们现在很多手机都取消了3.5mm手机孔,这让我们使用3.5mm 耳机的选手十分头疼,还好出了接口转换器,我们又可以使用3.5mm 耳机听音乐了。我们接下来实现一下这个转换的逻辑。
代码
Type-C 接口的手机
/**
* MI9手机
*/
public class TypeCPhone {
public void outputMusic() {
System.out.println("Type-C 插孔 -- 输出音乐源");
}
}
在这里我们简单实现一个音乐输出的方法。
3.5mm 耳机孔接口
/**
* 适配3.5mm耳机孔
*/
public interface ThreePointFive {
void requireOutputMusic() ;
}
这里简单定义了 需要转换后输出音乐源。
转换器(适配器)
/**
* 将Type-c 输出源转换为 3.5mm手机输出源
*/
public class Adapter implements ThreePointFive{
private TypeCPhone typeCPhone = new TypeCPhone();
public Adapter() {
System.out.println("获取Type-C 输出源 ");
}
@Override
public void requireOutputMusic() {
typeCPhone.outputMusic();
System.out.println("Type-C 输出源开始转换为3.5mm 输出源");
}
}
在这里我们使用了对象适配的模式。
转换测试类
public class Test {
public static void main(String[] args) {
ThreePointFive threePointFive = new Adapter();
threePointFive.requireOutputMusic();
}
}
结果
其他开源中的实现
我们来看一下 Spring 中 AOP中实现的一个 适配器模式。
package org.springframework.aop.framework.adapter;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.Advisor;
public interface AdvisorAdapter {
boolean supportsAdvice(Advice advice);
MethodInterceptor getInterceptor(Advisor advisor);
}
我们可以看到 这是一个切面拦截适配接口,我们可以实现 方法执行前后的拦截适配作用,我们来看一下具体实现类。
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
通过源码,我们可以很清楚的看到,适配器在AOP中拦截方法时的实现。
小结
适配器模式在工程维护中,可以起到高复用的效果。希望小伙伴们不要在开发中盲目的重复实现已有类似的逻辑。