一、适配器模式
一句话总结:有个目标类和目标接口,目标接口不符合我们的要求,于是定义一个适配者,实现目标接口,在适配者中进行自己的实现,使它能满足自己的需求
- 定义:
将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作
- 类型:结构型
- 适用场景:
- 已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)
不是软件设计阶段考虑的设计模式
,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案
- 优点:
- 能提高类的透明性和复用,现有的类复用但不需要改变,解决了现有类和目标类不匹配的问题
- 目标类和适配器类解耦,提高程序扩展性
- 符合
开闭原则
,具体的实现都在适配器中,客户端知道的只有适配器类,扩展也只需要扩展适配器类即可
- 缺点:
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性
- 增加系统代码可读的难度
- 扩展:
- 对象适配器:符合组合复用原则,使用委托机制
- 类适配器:通过类继承来实现
- 相关设计模式:
- 适配器模式和
外观模式
:都是对现有类,现有系统的封装,外观定义了新的接口,而适配器则是复用原有的接口,适配器是两个已有的接口协同工作,而外观则是在现有的系统中提供一个更为方便的访问入口,外观模式是适配整个子系统,所以外观针对的对象力度更大 - 适配器模式和
装饰者模式
:装饰者模式重点在于增强,适配器模式重点在于适配
二、类适配器 Coding
public interface Target {
void request();
}
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("concreteTarget目标方法");
}
}
public class Adaptee {
public void adapteeRequest(){
System.out.println("被适配者的方法");
}
}
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
super.adapteeRequest();
}
}
public class Test {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
- UML类图
![类适配器UML](https://i-blog.csdnimg.cn/blog_migrate/2901bc39e6789c948cf118378a2ad1f0.png)
- 说明:可以看到测试方法中,上面两行是目标的普通实现,下面是目标的适配器实现,目标(Target)的具体实现通过Adapter已经移交给了Adaptee,这个是类适配器模式,这里强调的是继承,通过继承来获取被适配者的一些方法,在实现Target的request方法中,可以增加各种逻辑代码。
三、对象适配器 Coding
- 其他类均未变,改变的是适配器类(Adapter),把原先的继承(被装饰者Adaptee)改成组合的方式
public interface Target {
void request();
}
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("concreteTarget目标方法");
}
}
public class Adaptee {
public void adapteeRequest(){
System.out.println("被适配者的方法");
}
}
public class Adapter implements Target{
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
adaptee.adapteeRequest();
}
}
public class Test {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
- UML类图
![对象适配器UML](https://i-blog.csdnimg.cn/blog_migrate/bd74d3cc9eaec4750a34fd41cf4c0979.png)
- 说明:其实两种方式的代码很相似,只不过一个是组合一个是继承,最后的效果都是一样的,目的都是达到适配。
四、实际场景实战
- 场景描述:将200V的交流电适配成5V的直流电用于给手机充电
- Coding:
- 220V的交流电
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输出交流电"+output+"V");
return output;
}
}
- 5V的直流电接口
public interface DC5 {
int outputDC5V();
}
- 适配器
public class PowerAdapter implements DC5{
private AC220 ac220 = new AC220();
@Override
public int outputDC5V() {
int adapterInput = ac220.outputAC220V();
int adapterOutput = adapterInput / 44;
System.out.println("使用PowerAdapter输入AC:" + adapterInput + "V" + "输出DC:" + adapterOutput + "V");
return adapterOutput;
}
}
- 测试类
public class Test {
public static void main(String[] args) {
DC5 dc5 = new PowerAdapter();
dc5.outputDC5V();
}
}
- 打印结果
输出交流电220V
使用PowerAdapter输入AC:220V输出DC:5V
五、源码中的应用
- javax.xml.bind.annotation.adapters.XmlAdapter
![XmlAdapter](https://i-blog.csdnimg.cn/blog_migrate/684d843358eb2b1b685b91fcc334cda0.png)
- org.springframework.aop.framework.adapter.AdvisorAdapter:Spring 源码中 AOP 相关的适配器,可以看到它的一个实现
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
MethodBeforeAdviceAdapter() {
}
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
- org.springframework.web.servlet.HandlerAdapter:SpringMVC流程熟悉的话,对于这个处理器适配器应该很熟悉了。