前面三篇文章分別學習了單例模式、三種工廠模式和建造者模式,它們都是比較常用的建立型模式,顧名思義就是建立物件的。從這篇文章開始來學習結構型設計模式,今天是第一篇——介面卡模式。
介面卡模式
首先拿我使用的小米手機為例,它撤銷了原來的 Audio 介面,要使用耳機聽歌呢,就必須使用 Type-C to Audio 轉接線(如下圖),一頭接上手機,一頭接上耳機線。(ps:話說,每次聽歌和充電都要換來換去的,好麻煩)
其實,這就是一個實際的介面卡,和設計模式中的介面卡扮演著同樣的角色,將一個介面轉換為另一個介面,以符合使用者的期望。
改進前
下面我們就將這個例子轉換為程式碼,看它是如何實現的。以前的手機呢,有兩個介面,使用 TypeC 介面充電:
public interface TypeCInterface {
// 充電
void chargeWithTypeC();
}
public class TypeCInterfaceImpl implements TypeCInterface {
@Override
public void chargeWithTypeC() {
System.out.println("使用 Type-C 介面充電");
}
}
複製程式碼
使用 Audio 介面聽歌:
public interface AudioInterface {
// 聽歌
void listenWithAudio();
}
public class AudioInterfaceImpl implements AudioInterface {
@Override
public void listenWithAudio() {
System.out.println("使用 Audio 介面聽歌");
}
}
複製程式碼
而我們的手機,同時有這兩個介面,用來充電和聽歌:
public class XiaomiPhone {
private AudioInterface audioInterface;
private TypeCInterface typeCInterface;
public XiaomiPhone(AudioInterface audioInterface, TypeCInterface typeCInterface) {
this.audioInterface = audioInterface;
this.typeCInterface = typeCInterface;
}
public void charge() {
typeCInterface.chargeWithTypeC();
}
public void listen() {
audioInterface.listenWithAudio();
}
}
複製程式碼
而我們就可以用手機來邊充電,邊聽歌了:
public class Client {
public static void main(String[] args) {
AudioInterface audioInterface = new AudioInterfaceImpl();
TypeCInterface typeCInterface = new TypeCInterfaceImpl();
XiaomiPhone xiaomiPhone = new XiaomiPhone(audioInterface, typeCInterface);
xiaomiPhone.charge();
xiaomiPhone.listen();
}
}
複製程式碼
改進了
本來這一切都好好的,可是小米手機把 Audio 介面取消了,我們沒法直接使用來聽歌了。於是,我們只好使用轉接線,將 Type-C 介面轉為 Audio 介面:
// 需將其轉換為 Audio 介面,所以實現了 AudioInterface
public class TypeCToAudioTieline implements AudioInterface {
private TypeCInterface typeCInterface;
// 另一頭是 TypeC,所以傳入 TypeCInterface
public TypeCToAudioTieline(TypeCInterface typeCInterface) {
this.typeCInterface = typeCInterface;
}
@Override
public void listenWithAudio() {
// ···
typeCInterface.chargeWithTypeC();
}
}
複製程式碼
然後呢,把轉接線插入到手機上(把手機和轉接線看作一個整體,它只有 Audio 介面了):
public class XiaomiPhone {
private AudioInterface audioInterface;
public XiaomiPhone(AudioInterface audioInterface) {
this.audioInterface = audioInterface;
}
public void listenWithAudio() {
audioInterface.listenWithAudio();
}
}
複製程式碼
於是,現在我們就通過轉接線,將 Type-C 介面轉換成了 Audio 介面。然後將耳機插在轉接線上,就可以聽歌了:
public class Client {
public static void main(String[] args) {
TypeCInterface typeCInterface = new TypeCInterfaceImpl();
TypeCToAudioTieline tieline = new TypeCToAudioTieline(typeCInterface);
XiaomiPhone xiaomiPhone = new XiaomiPhone(tieline);
xiaomiPhone.listenWithAudio();
}
}
複製程式碼
上述模式就是介面卡模式,它將一個類的介面轉換成使用者所需要的另一個介面,使得原本介面不相容的類可以一起工作。
它的 UML 圖如下:
下面我們來總結介面卡模式的優點:
- 它可以通過介面卡進行介面的轉換,讓原本不相容的類協同工作;
- 這可以使客戶從實現的介面解耦,如果被適配者改變了介面,介面卡可以將其封裝起來,客戶不必跟隨其修改;
缺點:
- 增加一個介面卡,可能會增加系統的複雜度。
介面卡模式的具體實踐
JDK#InputStreamReader
通過 InputStreamReader,可以將 InputStream 位元組流轉換為字元流進行處理。
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
// 將 inputStream 轉換為 InputStreamReader
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
public int read() throws IOException {
return sd.read();
}
}
複製程式碼
Spring#AOP/MVC
另外,比如在 Spring AOP 中,將 Advice 封裝成對應的攔截器型別。或是在 Spring MVC 中,通過介面卡模式,用於執行目標 Controller 中的請求處理方法。
由於對其原始碼不太熟悉,這裡也就不詳細說了。感興趣的小夥伴可以看看這篇文章。