适配器模式
此模式难度系数为初级,由Gang Of Four提出。
适配器模式是作为两个不兼容的接口之间的桥梁,这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。
此模式一般应用于已经存在的类接口中,有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
意图
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用:
1、系统需要使用现有的类,而此类的接口不符合系统的需要。
2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口
解释
现实世界中的例子
读卡器作为内存SD卡和电脑之间的适配器,你将内存SD卡插入读卡器,再将读卡器插入电脑USB接口,这样电脑可以通过读卡器来读取SD卡中的数据
另一个例子是著名的电源适配器;三脚插头不能连接到双管插座,它需要使用一个电源适配器,使其与双管插座兼容。还有一个例子是翻译人员将一个人所说的话翻译成另一个人所说的话
简而言之
适配器模式允许你在适配器中包装一个不兼容的对象,使其与另一个类兼容
维基百科
>In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code(在软件工程中,适配器模式是一种软件设计模式,允许现有类的接口用作另一个接口。它通常用于使现有类与其他类一起工作,而不修改它们的源代码)
程序示例
考虑一个只能使用划艇而根本不能航行的船长。
首先我们有一个接口划艇RowingBoat和渔船FishingBoat
/**
* The interface expected by the client.<br>
* A rowing boat is rowed to move.
*
*/
public interface RowingBoat {
void row();
}
/**
*
* Device class (adaptee in the pattern). We want to reuse this class.
* Fishing boat moves by sailing.
*
*/
public class FishingBoat {
private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
public void sail() {
LOGGER.info("The fishing boat is sailing");
}
}
接下来定义Captain船长类,船长Uses RowingBoat
/**
* The Captain uses {@link RowingBoat} to sail. <br>
* This is the client in the pattern.
*/
public class Captain {
private RowingBoat rowingBoat;
public Captain() {}
public Captain(RowingBoat rowingBoat) {
this.rowingBoat = rowingBoat;
}
public void setRowingBoat(RowingBoat rowingBoat) {
this.rowingBoat = rowingBoat;
}
public void row() {
rowingBoat.row();
}
}
现在,假设海盗来了,我们的船长需要逃跑,但是只有渔船可用。我们需要创造一个适配器,让船长能够用他的划艇技能操作渔船
/**
*
* Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link RowingBoat}
* interface expected by the client ({@link Captain}).
*
*/
public class FishingBoatAdapter implements RowingBoat {
private FishingBoat boat;
public FishingBoatAdapter() {
boat = new FishingBoat();
}
@Override
public void row() {
boat.sail();
}
}
最后”船长“使用渔船逃脱了海盗的追击
public class App {
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
// The captain can only operate rowing boats but with adapter he is able to use fishing boats as well
Captain captain = new Captain(new FishingBoatAdapter());
captain.row();
}
}
结果
被适配的对象和类适配器有不同的结果取舍。
类适配器:
- 通过提交一个具体的适配器类使适配对象适应目标。因此,当我们想要调整一个适配对象类及其所有子类时,类适配器将不起作用
- 只引入一个对象,不需要额外的指针间接指向适配对象。
- 让适配器重写适配对象的一些方法,因为适配器是适配对象的子类。
适配对象:
- 允许我们用一个适配器来处理许多适配对象,即适配对象本身及其所有子类(如果有的话)。适配器还可以同时向所有适配对象添加功能。
- 使得更难重写适配对象的方法。这将需要对所有的适配对象进行子类化,并使适配器引用适配对象子类而不是适配对象本身。
Java中的现实例子
- java.util.Arrays#asList()
- java.util.Collections#list()
- java.util.Collections#enumeration()
- javax.xml.bind.annotation.adapters.XMLAdapter
写在最后
适配器对象是我们接触到的第一个结构型模式,需要注意的是适配器模式一般不是在新的程序设计时使用和添加的,而是在解决正在服役的项目内外接口兼容性问题的。
说实话,我们应该熟练掌握这种设计模式,因为大多数时候我们是来解决现役项目问题的。
因此接下来我们再来编写一个适配器模式小程序,就以上面经典的电脑读取内存SD卡的数据为例。
首先电脑通过USB接口可以读取读卡器中的数据,而读卡器可以读取SD内存卡中的数据。按此思路画出程序UML类图如下:
根据类图我们开始编写程序,步骤一定义电脑扩展接口ComputerExtendInterface和MicroSD闪存卡类:
public interface ComputerExtendInterface {
void usbReadData();
}
public class MicroSD
{
private static Logger loggger = LoggerFactory.getLogger(MicroSD.class);
public void readSD(){
loggger.info("reading data from Micro SD....");
}
}
步骤二定义适配器类读卡器:
/**
* 适配MicroSD 到ComputerExtendInterface 接口
*/
public class ReadCardAdapter implements ComputerExtendInterface{
private MicroSD microSD;
public ReadCardAdapter(){
this.microSD = new MicroSD();
}
@Override
public void usbReadData() {
microSD.readSD();
}
}
步骤三定义适配客户端电脑类:
public class Computer {
private ComputerExtendInterface extendInterface;
public Computer(ReadCardAdapter readCardAdapter){
this.extendInterface = readCardAdapter;
}
/**
* 读取数据
*/
public void readData(){
extendInterface.usbReadData();
}
}
步骤四完成App的调用编写:
public class App {
public static void main(String[] args) {
Computer computer = new Computer(new ReadCardAdapter());
computer.readData();
}
}
运行App输出结果如下:
19:39:48.552 [main] INFO com.lyp.adapter.MicroSD - reading data from Micro SD....
下一篇文章我们将学习结构性模式中的桥接模式(Bridge Pattern)
码字不易,各位看官如果喜欢的话,请给点个喜欢 ️,关注下我,我将努力持续不断的为大家更新