去英语国家旅游时,我们只会说中文,为了与当地人交流,我们需要购买个翻译器,将中文翻译成英文,而这运用了适配器模式。
1 概述
适配器模式(Adapter Pattern),将一个接口转换成客户喜欢的另一个接口,使接口不兼容的那些类可以一起工作。这里的接口指广义的接口,它可以表示一个方法或者一组方法的集合。适配器又称为包装器。
图 对象适配器模式
Target: 目标抽象类,定义客户所需接口。
Adapter:适配器类,可以调用另一个接口,作为一个适配器,对Adaptee和Target进行匹配。
Adaptee: 被适配者类,定义了一个已经存在的接口,这个接口需要适配。被适配者类一般是一个具体的类。
public class ChineseAdaptee {
public void speakChinese(String content) {
System.out.println(content);
}
}
public interface EnglishTarget {
void speakEnglish(String content);
}
public class EnglishToChineseAdapter implements EnglishTarget{
private final ChineseAdaptee chineseAdaptee;
public EnglishToChineseAdapter(ChineseAdaptee chineseAdaptee) {
this.chineseAdaptee = chineseAdaptee;
}
@Override
public void speakEnglish(String content) {
if ("hello".equals(content)) {
chineseAdaptee.speakChinese("你好");
} else {
chineseAdaptee.speakChinese("对不起,听不懂。");
}
}
}
public class Client {
public static void main(String[] args) {
ChineseAdaptee chineseAdaptee = new ChineseAdaptee();
EnglishTarget englishTarget = new EnglishToChineseAdapter(chineseAdaptee);
englishTarget.speakEnglish("hello");
englishTarget.speakEnglish("nice to meet you");
// 运行结果:
// 你好
// 对不起,听不懂。
}
}
1.1 类适配器模式
类适配器和对象适配器最大的区别在于其匹配器和适配者之间的关系是继承关系。
图 类适配器模式
由于Java语言不支持多重类继承,因此类适配器模式的使用受到了很多限制。
需求:老代码中有一个加密算法,把二进制数据使用md5算法加密,生成一个字符串。新需求中,要求对用户输入的“用户名+密码”字符串进行md5加密。
图 加密需求设计方案
public class Md5Adaptee {
public String md5Encrypt(String str) {
return "md5加密" + str;
}
}
public interface UserInfoEncryptTarget {
String encrypt(String username,String password);
}
public class UserInfoEncryptAdapter extends Md5Adaptee implements UserInfoEncryptTarget{
@Override
public String encrypt(String username, String password) {
return md5Encrypt(username + password);
}
}
public class Client {
public static void main(String[] args) {
UserInfoEncryptTarget userInfoEncryptTarget = new UserInfoEncryptAdapter();
System.out.println(userInfoEncryptTarget.encrypt("admin","123456"));
}
}
1.2 缺省适配器模式(接口适配器)
当不需要实现一个接口所提供的方法时,可以先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现,那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求。
它适用于不想使用一个接口中的所有方法的情况。
图 接口适配器
ServiceInterface: 适配者接口,通常在该接口声名了大量的方法。
AbstractService: 缺省适配器类,使用空方法形式实现了在ServiceInterface接口中声名的方法。通常将它定义为抽象类,因为对它进行实例化没有任何意义。
ConcreteService: 具体业务类,继承了缺省适配器类,可以根据需要有选择性地覆盖在适配器类中定义的方法。
需求:有个文件流接口,定义了读文件、写文件及文件安全性验证三个方法。现在想定义一个类,用来验证文件安全性,而不想实现其他方法。
public interface FileInterface {
void writeFile(String path, OutputStream outputStream);
void readFile(String path);
void verifyFile(String path);
}
public abstract class FileAbstract implements FileInterface{
@Override
public void writeFile(String path, OutputStream outputStream) {
throw new RuntimeException("不能操作这个方法");
}
@Override
public void readFile(String path) {
throw new RuntimeException("不能操作这个方法");
}
@Override
public void verifyFile(String path) {
throw new RuntimeException("不能操作这个方法");
}
}
public class VerifyFile extends FileAbstract{
@Override
public void verifyFile(String path) {
System.out.println("安全性验证:" + path);
}
}
public class Client {
public static void main(String[] args) {
FileInterface fileInterface = new VerifyFile();
fileInterface.verifyFile("hello.java");
}
}
适配器名称 | 实现 | 作用及优势 |
对象适配器 | 在适配类中关联一个被适配者对象,通过调用被适配者对象的方法来实现适配。 | 适配目标接口。扩展方便,可动态替换被适配者,来适配其他类。 |
类适配器 | 适配类继承被适配者类。 | 适配目标接口。使用继承方式,不方便扩展,但是实现方法比较方便。 |
接口适配器 | 通过一个抽象类来实现接口中的所有方法,具体类继承抽象类,根据需求重写特定的方法。 | 不想实现一个接口中的所有方法。 |
表 三种类型适配器模式对比
2 优缺点
优点:
- 将目标类和适配者类解耦,通过引入一个适配器来重用现有适配者类的功能,无须修改原有接口。
- 灵活性和扩展性很好,可以动态替换被替换者及适配器,符合开闭原则。
- 对于不想实现接口中所有方法的场景,适配器模式能按需求重写需要实现的方法。
缺点:
- Java不支持多重类继承,类适配器一次最多只能适配一个适配器。且目标抽象类只能为接口。有一定局限性。
3 适用场景
- 想适配老代码的相关接口。
- 不想实现接口中的所有方法。