Java常用設計模式————適配器模式

引言

由於無法直接使用某個類中的方法而採取的一種中間類轉換的策略。將一個類的接口轉換成另一個接口,讓原本接口不兼容的類可以兼容。

適配器模式可以分爲三種:類適配器、對象適配器、接口適配器。它們之間的區別主要體現在適配器角色與被適配角色之間的依賴關係上。如類適配器是通過繼承的方式,令適配器繼承被適配類。

我們可以將適配器理解爲兩個不兼容的接口之間的橋樑。這是一種結構型模式。

雖然解決了老接口與新代碼之間的兼容問題,但是適配器模式也存在不容忽視的缺點。過多的使用適配器會讓系統變得凌亂,不易整體把握,因此,如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。

注意,適配器不是在詳細設計時添加的,而是解決正在服役的項目的兼容問題時添加的,而且不可以大量使用

一、類適配器

首先描述一下具體場景。

以電壓適配爲例。假設目前有一個用於輸出電壓的接口 Voltage,提供一個 output() 輸出電壓方法,且有一個可以輸出220V標準家庭電壓的實現類 Voltage220V。目前的系統中有一些家用電器的類,如TV(電視機類)、Fridge(電冰箱類)等,它們有一個charge() 方法,需要依賴 Voltage.output() 提供的電壓爲其充電。

Voltage220V的output()方法實現了 220V電壓的輸出功能,所以一切安好,但是今天我們買了一部手機,需要 5V的電壓爲其充電,但是目前系統中只有220V電壓的輸出,這時我們該如何解決呢?如下的類圖所示:

此時我們最常想到的是再新建一個 Voltage 的實現類,可以叫 Voltage5V,然後實現其output() 方法。如下所示:

這種方法是一種解決思路,這並沒有什麼問題,實際上,在真正的生產開發中,也有很多是以這種方式解決的。當然,如果真的能夠以這種方式解決問題是最好的。

但是,如果系統中 220V 電壓是一個標準電壓,我們無法新加一個與之等價的類,或者必須要基於已有的實現來解決的話,那麼適配器模式就派上了用場,這也是適配器模式的一個主要特點,就是對已有方法的複用。於是,就有了下面這種適配器模式的類圖:

代碼實現如下:

/**
 * 電壓接口
 */
public interface Voltage {
    /** 電壓輸出*/
	int output();
}
/**
 * 220V電壓實現
 */
public class Voltage220V implements Voltage {

	private static final int voltage = 220;

	@Override
	public int output() {
		System.out.println("輸出" + voltage + "電壓");
		return voltage;
	}
}
/**
 * 220V電壓適配器
 */
public class Voltage220VAdapter extends Voltage220V implements Voltage {

	@Override
	public int output() {
		int superVoltage = super.output();
		System.out.println("父類輸出電壓:" + superVoltage + "V");
		int adaptedVoltage = superVoltage / 44;
		System.out.println("已將" + superVoltage + "V電壓爲" + adaptedVoltage + "V。");
		return adaptedVoltage;
	}
}
/**
 * 手機類
 */
public class Phone {
	/**
	 * 充電需要電壓:5V
	 */
	public void charge(Voltage voltage) {
		if (voltage.output() == 5) {
			System.out.println("電壓爲5V,正在充電~");
		} else {
			System.out.println("電壓異常,手機未充電!");
		}
	}
}

以上代碼是適配器中幾個關鍵角色,其中Voltage220V代表被適配類,Voltage220VAdapter 就代表220V電壓的適配器類。以下是測試代碼:

public class Test {
	public static void main(String[] args) {
		Phone phone = new Phone();
		phone.charge(new Voltage220VAdapter());
	}
}
// 輸出:
輸出220電壓
父類輸出電壓:220V
已將220V電壓爲5V。
電壓爲5V,正在充電~

二、對象適配器

對象適配器與類適配器的區別是與被適配器類的關係,由泛化關係(繼承)變爲依賴關係(組合或聚合)。如下圖所示:

可以看到,適配器類Voltage220VAdapter以組合的方式將Voltage220V的一個對象引用到其內部,這在一定程度上避免了因爲繼承關係帶來的較強的耦合度。同時,該適配器類可以更加靈活,去適配更多的電壓,如1000V等等。代碼如下:

/**
 * 220V電壓適配器
 */
public class Voltage220VAdapter implements Voltage {
	
	private Voltage220V v220;
	
	public Voltage220VAdapter(Voltage220V v220) {
		this.v220 = v220;
	}
	
	@Override
	public int output() {
		int srcVoltage = v220.output();
		System.out.println("原輸出電壓:" + srcVoltage + "V");
		int adaptedVoltage = srcVoltage / 44;
		System.out.println("已將" + srcVoltage + "V電壓爲" + adaptedVoltage + "V。");
		return adaptedVoltage;
	}
}

對象適配器與類適配器唯一的不同僅僅是對被適配類的依賴關係,由繼承變爲了組合或聚合,因此只有適配器本身稍有變化,去掉了繼承關係,增加了一個 v220 字段。其他的接口都沒什麼變化。在測試代碼中,我們需要爲適配器類的構造器傳入一個被適配的對象,如下所示:

public class Test {
	public static void main(String[] args) {
		Phone phone = new Phone();
		phone.charge(new Voltage220VAdapter(new Voltage220V()));
	}
}
// 輸出:
輸出220電壓
原輸出電壓:220V
已將220V電壓爲5V。
電壓爲5V,正在充電~

三、接口適配器

接口適配器模式,也叫缺省適配器模式。

當不需要全部實現接口提供的方法時,可先設計一個抽象類實現接口,併爲該接口中每個方法提供一個默認的空方法實現,這樣,該抽象類的子類就可以有選擇地覆蓋父類的某些方法來實現需求。

接口適配器其實挺好理解的,在很多類庫框架中也會用到,經常會看到一些方法僅僅是一個空方法,其實就用到這種接口適配器模式。相比於類適配器或對象適配器,這種接口適配器並不是爲了實現某種兼容採用的解決辦法,而更多的是爲了保證各個子類的功能保持“單一職責原則”,避免將接口中不需要的方法暴露給外部調用者。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章