Adaptor Pattern 轉接器模式 [Design Pattern in Java]

Posted by Kubeguts on 2020-07-29

假如要使用A, 但無法直接使用, 只能使用B介面

所以我們用轉接器類別包裝A的方法,並且實作B,達到轉接的效果

用螢幕轉接器的例子直接解釋

例如我們有個 HDMI的線, 叫做 HDMI interface

1
2
3
4
5

interface HDMI {
// 定義連接裝置的方法格式
boolean connect(String device);
}

但現在有個螢幕只有支援VGA的插孔

所以我們需要個 HDMI轉VGA的Adaptor類別, 把VGA的類別引入進來, 並使用之

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

class HdmiToVgaAdpator implements HDMI {
VGA vgaCable = null;

HdmiToVgaAdpator(VGA vgaCable) {
this.vgaCable = vgaCable;
}

// 必須要實作HDMI的方法
public boolean connect(String device) {
// 介接Vga線的connect功能
System.out.println("使用轉接器的connect功能!");
vgaCable.connect(device);
return true;
}
}

因為有使用到VGA類別, 所以來定義一下

1
2
3
4
5
6
7
8
9
10
11
12

interface VGA {
// 定義連接裝置的方法格式
boolean connect(String device);
}

class VGACable implements VGA {
public boolean connect(String device) {
System.out.println("VGA線連接: " + device);
return true;
}
}

來定義有台筆電, 是只有具備hdmi孔, 但具備著可以連接到某裝置進行投影

1
2
3
4
5
6
7
class Labtop {
// 該台筆電只有hdmi孔, 並指定要連接的螢幕(device)
public void connectToDevice(HDMI hdmi, String device) {
System.out.println("筆電只有hdmi孔, 使用實作hdmi介面的 'hdmi轉vga的轉接器' 類別!");
hdmi.connect(device);
}
}

接著來使用吧

1
2
3
4
5
6
7
8
9
10
11
public class main {
public static void main(String[] args) {

Labtop laptop = new Labtop();
// 如果某筆電只有hdmi插孔, 但要接的投影螢幕只有vga
laptop.connectToDevice(
new HdmiToVgaAdpator( // 這時可以使用Hdmi轉Vga的轉接器! 轉接器需要帶入實際具有VGA功能的VGA線物件
new VGACable()),
"老舊投影機");
}
}

我們可以看到結果為:

  1. 筆電使用了 實作hdmi介面的轉接器 (轉接器因為實作了hdmi interface, 所以可以插入筆電的hdmi孔)
  2. 接著轉接器引入了VGA cable類別, 故轉接器就可以使用VGA的connect()功能
  3. 最後就觸發VGA功能, 與投影裝置連動

實際應用場景

通常轉接器模式可用在轉接那些已經過時的模組&方法

例如有模組是用Java較舊迭代Collection的版本去寫的, 為Enumeration Interface

但現在新的專案中,是使用Iterator方式去迭代Collection

1
2
3
4
5
6
7
8
9
10
class NewLoopService {
// 使用Java 5版本之後支援的Iterator
public void loopCollection(Collection col, Iterator itr) {
itr = col.iterator();

while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}

使用NewLoopService

1
2
3
4
5
6
7
8
9
10
11
12
13
class main() {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");

// 宣告Iterator的變數
Iterator itr;

NewLoopService loopService = new NewLoopService();
loopService.loopCollection(list, itr);
}
}

但現在若只能用舊版的 Enumeration 方式去迭代

這時就可以寫一個 EnumeratorAdaptor 來實作可兼容Iterator的類別, 並可呼叫 Enumeration的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 實作Iterator的方法
class EnumeratorAdaptor<E> implement Iterator {
Enumeration<E> enumeration;

EnumeratorAdaptor(Enumeration<E> enumeration) {
this.enumeration = enumeration;
}

// 實作Iterator 介面所需的方法
public boolean hasNext() {
// 但實際上是使用Enumeration的方法
return this.enumeration.hasMoreElements();
}

public Object next() {
return this.enumeration.nextElement();
}

// 但這時候會遇到Enumeration沒有的方法, 但又必須要實作 滿足Iterator的介面規範, 這時可以throw Exception來處理
public void remove() {
throw new UnsupportedOperationException();
}
}

這時我們可以在main()中改用轉接器, 來間接使用Enumeration了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class main() {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");

// 使用 `實作Iterator介面` 的轉接器
EnumeratorAdaptor itr;

NewLoopService loopService = new NewLoopService();
loopService.loopCollection(list, itr);
}
}


class NewLoopService {
// 使用Java 5版本之後支援的Iterator
public void loopCollection(Collection col, Iterator itr) {
itr = col.iterator();

// 這時會是使用EnumeratorAdaptor內的hasNext()與next() 達到轉接的效果
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}

透過以上方法, 可以把要使用Enumeration方式的service, 但該Service又是只能吃Iterator的輸入, 那就可以實作一個 EnumeratorAdaptor, 帶入Service, 並透過轉接器使用 Enumeration的方式去撈出Collection內容.