1 概述
适配器模式(Adapter Pattern),从名字就可以看出,工作模式类似于适配器:将原本不兼容的两样事物连接,以协同工作。
2 适配器模式
充电器(电源适配器)是日常生活中常见的例子。大多手机要求输入电压是5V
,而家用交流电的电压都是220V
,充电器作为适配器,将220V
的电压转为目标电器需要的电压。适配器模式也类似,通过适配器,将类的接口转换为目标所期望的另一个接口。
适配器模式是开闭原则的体现,通过增加一个适配类,避免了对原有逻辑的修改。
3 案例
适配器模式主要有三种类型:类适配器,对象适配器,默认适配器。下面举例说明。
3.1 类适配器
类适配器使用的是继承模式,适配器类即是A接口,又是B接口。下面的例子展现了,如何通过适配器,使得“中国人”说“英语”:
public class Test {
public static void main(String[] args) {
// 普通ChineseSpeaker对象,只能输出中文
ChineseSpeaker xiaoming = new ChinesePeople();
xiaoming.speak();
// 被适配过的ChineseSpeaker对象,可以输出英文
ChineseSpeaker englishTeacher = new ChinesePeopleEnglishAdapter();
englishTeacher.speak();
}
}
public interface EnglishSpeaker {
void talk();
}
public class EnglishPeople implements EnglishSpeaker {
[@Override](https://my.oschina.net/u/1162528)
public void talk() {
System.out.println("Hello!");
}
}
public interface ChineseSpeaker {
void speak();
}
public class ChinesePeople implements ChineseSpeaker {
[@Override](https://my.oschina.net/u/1162528)
public void speak() {
System.out.println("大家好");
}
}
// 适配器类继承了EnglishPeople,将speakChinese方法适配为speakEnglish方法
public class ChinesePeopleEnglishAdapter extends EnglishPeople implements ChineseSpeaker {
[@Override](https://my.oschina.net/u/1162528)
public void speak() {
this.talk();
}
}
输出:
大家好
Hello!
ChineseSpeaker
与EnglishSpeaker
是两个不同的接口,而通过适配器类ChinesePeopleEnglishAdapter
,使得ChineseSpeaker
对象可以调用EnglishSpeaker
的方法。
3.2 对象适配器
对象适配器使用的是组合模式,适配器实现A接口,并持有B接口的实例。下面的例子展现了,如何通过适配器,使得“普通人“可以“飞行”:
public class Test {
public static void main(String[] args) {
// 普通的Person对象,只能“跑”
Person blackWidow = new Mortal("Natasha");
blackWidow.move();
IronSuit suit = new IronSuit();
// 被适配了的对象,可以实现“飞”
Person ironMan = new FlyablePersonAdapter("Tony Stark", suit);
ironMan.move();
}
}
public interface Person {
void move();
}
public class Mortal implements Person {
private String name;
Mortal(String name) {
this.name = name;
}
[@Override](https://my.oschina.net/u/1162528)
public void move() {
System.out.println(name + " is running!");
}
}
public interface Flyable {
void fly();
}
public class IronSuit implements Flyable {
[@Override](https://my.oschina.net/u/1162528)
public void fly() {
System.out.println("I'm flying!");
}
}
// 适配器类持有IronSuit实例,将move方法适配为fly方法
public class FlyablePersonAdapter implements Person {
private String name;
IronSuit suit;
FlyablePersonAdapter(String name, IronSuit suit) {
this.name = name;
this.suit = suit;
}
@Override
public void move() {
System.out.print(name + " is wearing Iron Suit: ");
suit.fly();
}
}
输出:
Natasha is running!
Tony Stark is wearing Iron Suit: I'm flying!
通过适配,可以让Person
的move()
方法变为Flyable
的fly()
方法。
3.3 默认适配器
默认适配器是适配器模式的变种,主要解决的问题是,当一个接口有多个方法时,有时候实现类只关心其中的部分方法。通过添加一个适配器类来给方法提供默认实现,可以实现这一需求:
public class Test {
public static void main(String[] args) {
People jay = new Singer();
jay.speak();
People yao = new Athlete();
yao.move();
}
}
public interface People {
void eat();
void sleep();
void move();
void speak();
}
public class PeopleAdapter implements People {
@Override
public void eat() {}
@Override
public void sleep() {}
@Override
public void move() {}
@Override
public void speak() {}
}
// 通过适配器,Athlete只需要实现move方法
public class Athlete extends PeopleAdapter {
@Override
public void move() {
System.out.println("Athlete is running.");
}
}
// 通过适配器,Singer只需要实现speak方法
public class Singer extends PeopleAdapter {
@Override
public void speak() {
System.out.println("Singer is singing.");
}
}
输出:
Singer is singing.
Athlete is running.
适配器类PeopleAdapter
给接口中的方法提供了默认的实现(或是空实现),使得其子类可以只关心自己需要的方法。
4 使用
前两种适配器模式,在给系统新增功能的时候非常有用,可以避免对原有逻辑的修改,降低系统的复杂度。比如JDK
中我们熟悉的Callable
接口,跟Runnable
一样,也可以新起一个线程。但这是JDK1.5
中新增的接口,而新起线程是由Runnable
的实现类Thread
中的native方法实现的,那如何在原有基础上,增加对Callable
支持呢?答案就是适配器模式:
public class FutureTask<V> implements RunnableFuture<V> {
private Callable<V> callable;
public FutureTask(Callable<V> callable) {
this.callable = callable;
}
public void run() {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}
}
Callable
都会被包装成一个FutureTask
的实例,FutureTask
实现了Runnable
接口,可以作为Runnable
和Callable
两个接口的适配器,这样,我们不需要对原先Runnable
的框架做任何修改。
而第三种适配器模式则主要运用在开发过程中,可以为我们减少很多工作,易于开发。比较广为人知的便是Netty
的ChannelHandlerAdapter,它为开发者提供了接口中方法的空实现,降低了接口使用的难度。
4 总结
适配器模式符合开闭原则。当需要使两个不兼容的接口一起工作时,适配器模式将是很好的选择。
来源:oschina
链接:https://my.oschina.net/u/2411391/blog/3219444