Design Pattern 101 — Adapter Pattern
สำหรับ Design Pattern ในกลุ่มของ Structural Pattern ของ GoF จะมาเริ่มกันที่ Adapter Pattern หลายคนนึกถึงปลั๊กไฟ สำหรับแปลงระบบหนึ่งไปเป็นอีกระบบหนึ่ง ในบทความนี้จะมาทบทวนและเรียนรู้ว่า Adapter Pattern ใช้สำหรับแก้ปัญหาอะไรและใช้งานอย่างไร
Adapter Pattern
Problem
ถ้าเรามีโทรศัพย์ iPhone ที่ใช้ Lightning port และ Android ใช้ Micro USB สำหรับชาร์จไฟ ซึ่งมี interface LightningPhone
สำหรับ iPhone และมี interface MicroUsbPhone
สำหรับ Android phone โทรศัพย์ทั้ง 2 แบบนี้ ไม่สามารถใช้งาน port ชาร์จไฟ สลับกันได้ โดยมีขั้นตอน use(Lightning|MicroUsb)()
เพื่อใช้ port และ recharge()
เพื่อชาร์จไฟโทรศัพย์
public interface LightningPhone {
void recharge();
void useLightning();
}public interface MicroUsbPhone {
void recharge();
void useMicroUsb();
}public class Iphone implements LightningPhone {
private boolean connected; public void recharge() {
if(connected) {
System.out.println("Recharge started");
System.out.println("Recharge finished");
} else {
System.out.println("Connect lightning first");
}
} public void useLightning() {
connected = true;
System.out.println("Lightning connected");
}
}public class Android implements MicroUsbPhone {
private boolean connected; public void useMicroUsb() {
connected = true;
System.out.println("MicroUsb connected");
} public void recharge() {
if(connected) {
System.out.println("Recharge started");
System.out.println("Recharge finished");
} else {
System.out.println("Connect MicroUsb first");
}
}
}
และใช้งาน iPhone และ Android
public static void main(String[] args) {
LightningPhone iphone = new Iphone();
MicroUsbPhone android = new Android(); iphone.useLightning();
iphone.recharge(); android.useMicroUsb();
android.recharge();
}>> Output
Lightning connected
Recharge started
Recharge finished
MicroUsb connected
Recharge started
Recharge finished
แต่ถ้าวันนึงเราลืมเอาสายชาร์จ Lightning port ของ iPhone มาละจะต้องทำอย่างไร
Solution
ดั้งนั้นเราจะต้องมีตัวแปลงระหว่าง Lightning port ไปเป็น Micro USB port ซึ่ง Adapter Pattern จะมาช่วยสำหรับปัญหานี้
โดยสร้าง class LightningToMicroUsbAdapter
ขึ้นมา implement MicroUsbPhone
และ inject LightningPhone
เข้าไปใน class ผ่าน constructor ใน useMicroUsb()
ก็เรียกใช้ useLightning()
ของ LightningPhone
และ recharge()
ก็เรียกใช้ recharge()
ของ LightningPhone
public class LightningToMicroUsbAdapter implements MicroUsbPhone {
private final LightningPhone lightningPhone; public LightningToMicroUsbAdapter(LightningPhone lightningPhone) {
this.lightningPhone = lightningPhone;
} @Override
public void useMicroUsb() {
System.out.println("MicroUsb connected");
lightningPhone.useLightning();
} @Override
public void recharge() {
lightningPhone.recharge();
}
}
และใช้งาน Adapter
public static void main(String[] args) {
LightningPhone iphone = new Iphone();
MicroUsbPhone android = new Android(); iphone.useLightning();
iphone.recharge(); android.useMicroUsb();
android.recharge(); MicroUsbPhone adapter = new LightningToMicroUsbAdapter(iphone);
adapter.useMicroUsb();
adapter.recharge();
}>> Output
Lightning connected
Recharge started
Recharge finished
MicroUsb connected
Recharge started
Recharge finished
MicroUsb connected
Lightning connected
Recharge started
Recharge finished
Client ใช้งาน Adapter อย่างไร
- Client เรียกใช้งาน Adapter โดย call ไปที่ method ของ target interface ของ Adapter
- Adapter จะแปลงสิ่งที่ Client เรียกใช้ไปเป็น หนึ่งหรือมากกว่า call ไปยัง Adaptee ด้วย adaptee interface
- Client จะได้รับผลลัพธ์ของการ call โดยที่ไม่รู้ว่าเบื้องหลัง Adapter แปลงค่านั้นมาอย่างไร
Adapter Pattern จะแปลง interface ของ class ไปเป็นอีก interface ที่ Client ต้องการ Adapter ทำให้ class ที่มี interface ที่เข้ากันไม่ได้ สามารถทำงานร่วมกันได้
ตอนนี้เรารู้แล้ว่า Adapter Pattern ให้เราใช้งาน client ด้วย interface ที่ไม่เข้ากันด้วยการสร้าง Adapter ที่ทำหน้าที่ในการแปลง interface ที่ไม่เข้ากัน เปรียบเสมือนว่า Client ถูก decouple จาก implementation ของ interface นั้น และถ้าเราคาดหวังว่า interface จะเปลี่ยนแปลง Adapter จะซ่อน (encapsulate) การเปลี่ยนแปลงนั้นไว้ โดยที่ Client ไม่ต้องรู้ว่ามีการเปลียนแปลง เพื่อทำงานกับ interface ที่ไม่เข้ากัน
สรุป
จากที่ได้ทบทวนและใช้งาน Adapter Pattern จะเห็นได้ว่า pattern นี้มีประโยชน์มากในการแปลงจาก class ที่เรามีอยู่กับ class ใหม่ที่เข้ากันไม่ได้ ด้วยการสร้าง Adapter class เพื่อทำหน้าที่แปลงค่าผ่านทาง interface ซึ่งเป็นรูปแบบของ Object Adapter แต่มี Adapter อีกแบบคือ Class Adapter ที่ไม่ได้ครอบคลุมในบทความนี้ เพราะใช้ multiple inheritance ซึ่ง Java ไม่รองรับ และตัวอย่างก็เอามาจาก Wiki 😅