今天是设计模式学习系列的第7篇–适配器模式。
开头三问?
- 什么是适配器模式?什么是类的适配器模式、对象的适配器模式、接口的适配器模式?
- 适配器模式和装饰者模式的区别?
- 适配器模式的使用场景?
带着这几个问题,开始我们今天的学习吧。
适配器模式解析
说到适配器模式,其实顾名思义不难理解,我举一个例子。
相信很多朋友都有去过香港买东西,比如我曾经去香港买了一个 ipad air,但是呢,港版的它的充电器插头和我们大陆的插座是不适配的。
但是我已经买回来了,这个插头可是不能直接充电的,那怎么办呢?这时候我去淘宝上买了一个转换器。
这样一来就可以愉快的使用了,那么上面这个例子中,适配器的作用就是位于港版插头和我们大陆插座之间,它的工作将大陆插座转换成港版插座,好让港版插头可以插入得到电力。其实 适配器模式和真实世界的 适配器 扮演着同样的角色:将一个接口转换成另一个接口,以符合客户的期望。
我们使用适配器模式的过程一般如下:
- 通过目标接口调用适配器的方法对适配器发出请求;
- 适配器使用被适配者接口把请求转换成被适配者的一个或多个接口;
- 客户接收到调用结果,但是感知不到适配器在起转换作用;
结合上面的例子,港版充电头就是客户,它要调用港版标准插座进行充电,而淘宝买的转换器就是适配器,它实现了一个目标接口(提供港版插座接口),被适配者就是我们的大陆插座接口; 需要注意的是,客户和被适配者是解耦的,一个不知道另一个。
定义适配器模式:
将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
类图如下:
适配器模式充满着良好的 OO 设计原则: 使用对象组合,提供修改的接口包装被适配者,这种做法还有额外的优点就是 被适配者的任何子类都可以搭配适配器使用。
核心思想就是有一个 Adaptee
类,拥有一个方法待适配,目标接口是 Target
,通过 Adapter
类将 Adaptee
的功能扩展到 Target
里。
下面来看段代码加深理解:
/**被适配者*/
public class Adaptee {
public void method1() {
System.out.println("this is original method!");
}
}
/**目标接口*/
public interface Target {
/**
* 与原类中的方法相同
*/
public void method1();
/**
* 新类的方法
*/
public void method2();
}
/**Adapter 实现 Target 接口,组合了Adaptee*/
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
super();
this.adaptee = adaptee;
}
@Override
public void method2() {
System.out.println("this is the target method!");
}
@Override
public void method1() {
adaptee.method1();
}
}
/**测试类*/
public class AdapterTest {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.method1();
target.method2();
}
}
上面我们已经定义了适配器模式,不过实际上适配器模式分为 “对象适配器” 、 “类适配器”和“接口”适配器。
上面我们画的类图是 对象适配器的,接下来一起看看什么是类适配器和接口适配器。
类适配器
区别就在于 Adapter
类继承 Adaptee
类,实现 Target
接口:
/**
* 类适配器
*/
public class Adapter extends Adaptee implements Target {
@Override
public void method2() {
System.out.println("this is the target method!");
}
}
接口适配器
第三种适配器模式是接口的适配器模式,接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图:
这个很好理解,在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要。
适配器和装饰者模式的区别?
学了适配器模式你可能会感觉和装饰者模式非常像,都是持有一个对象实例,然后进行功能组装。
不过两者的侧重点吗是不同的: 适配器是将一个对象包装起来以改变其接口;装饰者将一个对象包装起来以增加新的行为和责任;而下一篇我准备写的外观模式是将一群对象 “包装” 起来以简化其接口,敬请期待。
这里还要注意: 装饰者不改变接口,只是加入责任,而适配器是将一个接口转成另一个接口;
适配者的使用场景
当需要使用一个现有的类而其接口并不符合你的需要时,就使用适配器。比如早起的JDK中集合类型的迭代器是 Enumeration 枚举。但是 后来新的 JDK 使用的是 Iterator,当我们对面遗留代码,这些代码暴露出枚举接口,但我们又希望在新的代码中只使用迭代器,解决这个问题我们就可以构造一个适配器,持有枚举 Enumeration 类的实例,即被适配者即可。
好了,适配器模式我们就学到这里。概念很好理解,希望大家定期按照本文开头一样,定期问自己这几个问题,不然这个东西非常容易忘记,当大脑梳理清楚后,在工作中写代码时多结合使用,相信你的代码会越写越顺滑。
全文完,fighting!