適配器模式是結構模式,包括類的結構模式和對象結構模式兩種。適配器模式把一個類(系統中已有的類)的接口變成客戶端所期待的另一種接口(需求,新的行爲增強功能),從而使原本因爲接口不匹配而無法再一起工作的兩個類能夠在一起工作。實質上是把已有的一些類應用適配器模式的結構進行包裝(Wrapper),來滿足需要的接口 (新的需求),達到軟件複用和擴展的目的。
一、類的適配器模式的結構
類圖與角色
在上圖中可以看到,Adaptee類並沒有sampleOperation2()方法,而客戶端則期待這個方法。爲了使客戶端能夠使用Adaptee類,提供一箇中間環節,即類Adapter,把Adaptee類的API和Target類的API銜接起來。Adapter和Adaptee是繼承關係,因此這個適配器模式是類的。
目標(Target)角色:這就是所期待得到的接口(新建的接口,包含新的需求)。注意,對於類適配器模式,目標不可以是類。
源(Adaptee)角色:現在需要適配的接口(這個是系統已有的類,適配器模式要複用的類)。
適配器(Adapter)角色:繼承源角色(Adaptee)並實現目標角色(Target)。適配器將源接口轉換爲目標接口。顯然,這一角色不可以是接口,而必須是具體類。
源代碼
目標角色Target是以Java接口的形式實現的。接口聲明瞭兩個方法:sampleOperation1()和sampleOperation2()。
Target的源代碼。
/**
* 目標角色
*/
public interface Target {
/**
* 這個是源類也有的方法
*/
void sampleOperation1();
/**
* 這個是源類沒有的方法
*/
void sampleOperation2();
}
源角色Adaptee是一個具體類,它有sampleOperation1()方法,但是沒有sampleOperation2()方法。
Adaptee類的源代碼。
/**
* 源角色
*/
public class Adaptee {
/**
* 源類含有的方法
*/
public void sampleOperation1(){
//code
}
}
適配器角色Adapter擴展了Adaptee類,同時又實現了目標接口。由於Adaptee沒有提供sampleOperation2()方法,而目標接口又要求有這個方法,因此適配器角色實現了這個方法。
Adapter類的源代碼。
/**
* 適配器角色(類的適配器模式)
*/
public class Adapter extends Adaptee implements Target{
/*
* 由於源類沒有該sampleOperation2()方法,
* 所有適配器類需要補充這個方法。
*/
@Override
public void sampleOperation2() {
//write your code here
}
}
二、對象的適配器模式的結構
結構與角色
從上圖中可以看到,Adaptee類並沒有sampleOperation2()方法,而客戶端則期待這個方法。爲了使客戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee類的實例,從而這個包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關係,因此這個適配器模式是對象的。
目標(Target)角色:這就是所期待得到的接口。目標可以是具體的類或者抽象類。
源(Adaptee)角色:現在需要適配的接口。
適配器(Adapter)角色:適配器是本模式的核心。適配器將源接口轉換爲目標接口。顯然,這一角色必須是具體類。
源代碼
目標Target類的源代碼。
/**
* 目標角色
*/
public interface Target {
/**
* 這個是源類也有的方法
*/
void sampleOperation1();
/**
* 這個是源類沒有的方法
*/
void sampleOperation2();
}
源Adaptee類源代碼。
/**
* 源角色
*/
public class Adaptee {
/**
* 源類含有的方法
*/
public void sampleOperation1(){
//code
}
}
適配器Adapter源代碼。
/**
* 適配器角色(對象的適配器模式)
*/
public class Adapter_2 implements Target{
private Adaptee mAdaptee;
public Adapter_2(Adaptee mAdaptee) {
this.mAdaptee=mAdaptee;
}
/*
* 源類有sampleOperation1()方法
* 因此適配類直接委派即可。
*/
@Override
public void sampleOperation1() {
mAdaptee.sampleOperation1();
}
/*
* 由於源類沒有該sampleOperation2()方法,
* 所有適配器類需要補充這個方法。
*/
@Override
public void sampleOperation2() {
//write your code here
}
}
需要明確的是,適配器模式的用意是將接口不同而功能相同或相近的兩個接口加以轉換,這裏麪包括適配器角色補充了一個源角色沒有的方法,但是這個方法是目標接口需要的方法。
三、在什麼情況下使用適配器模式
在以下情況下使用適配器模式
(1) 系統需要使用現有的類,而此類的接口不符合系統的需要。
(2) 想要建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括在將來可能要引進的一些類一起工作。
(3) 在設計裏,需要改變多個已有的子類的接口,可以使用對象的適配器模式。
四、適配器模式的例子
(1)需求:系統已有一個Car類表示普通汽車。現在需要爲普通汽車增加GPS定位功能,也就是需要一個新的帶有定位功能的汽車GPSCar類。
系統設計:
Car類是源角色。Car類的源代碼。
/**
* 源角色
*/
public class Car {
private String name;
private double speed;
public Car() {
}
public Car(String name,double speed) {
this.name=name;
this.speed=speed;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("車名:" + name + ",");
sb.append("速度:" + speed + "千米/小時");
return sb.toString();
}
}
需要定義一個接口GPS,該接口聲明瞭getLocation()方法,用來確定汽車的位置。GPS是目標角色。
GPS接口的源代碼。
/**
* 目標角色
*/
public interface GPS {
Point getLocation();
}
最後GPSCar類是需求的對象,是適配器角色。GPSCar類繼承Car並實現GPS接口。在該類中首先實現getLocation()方法,用於實現確定汽車位置的功能。
GPSCar類的源代碼。
/**
* 適配器角色
*/
public class GPSCar extends Car implements GPS {
@Override
public Point getLocation() {
Point point = new Point();
return point.setLocation(super.getSpeed(), super.getSpeed());
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append(",座標:(" + getLocation().getX()+ "," + getLocation().getY() + ")");
return sb.toString();
}
}
Point類的源代碼。/**
* 座標類
*/
public class Point {
private double x;
private double y;
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public Point setLocation(double x,double y){
Point point=new Point();
point.setX(x);
point.setY(y);
return point;
}
}
(2)需求:有一個“源”Person類是一個對象人,他擁有2種技能分別是說日語和說英語,而某個崗位(目標)Job需要你同時會說日語、英語、和法語,現在我們的任務就是要將人這個“源”適配的這個崗位中。
Person類源碼。
/**
* 源角色
*/
public class Person {
private String name;
private String sex;
private int age;
public void speakJapanese(){
System.out.println("Japanese");
}
public void speakEnglish(){
System.out.println("English");
}
//省略屬性get和set方法
}
目標接口Job源碼。
/**
* 目標角色
*/
public interface Job {
public abstract void speakJapanese();
public abstract void speakEnglish();
public abstract void speakFrench();
}
適配器模式實現一:類的適配器模式
適配器AdapterClass源碼。
/**
* 類的適配器模式,適配器角色
*/
public class AdapterClass extends Person implements Job{
@Override
public void speakFrench() {
System.out.println("French");
}
}
適配器模式實現二:對象的適配器模式
適配器AdapterObject源碼。
/**
* 對象的適配器模式,適配器角色
*/
public class AdapterObject implements Job{
private Person person;
public AdapterObject(Person person) {
this.person=person;
}
@Override
public void speakJapanese() {
person.speakJapanese();
}
@Override
public void speakEnglish() {
person.speakEnglish();
}
@Override
public void speakFrench() {
System.out.println("French");
}
}