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
public void talk() {
System.out.println("Hello!");
}
}
public interface ChineseSpeaker {
void speak();
}
public class ChinesePeople implements ChineseSpeaker {
@Override
public void speak() {
System.out.println("大家好");
}
}
// 適配器類繼承了EnglishPeople,將speakChinese方法適配爲speakEnglish方法
public class ChinesePeopleEnglishAdapter extends EnglishPeople implements ChineseSpeaker {
@Override
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
public void move() {
System.out.println(name + " is running!");
}
}
public interface Flyable {
void fly();
}
public class IronSuit implements Flyable {
@Override
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 總結
適配器模式符合開閉原則。當需要使兩個不兼容的接口一起工作時,適配器模式將是很好的選擇。