Design Pattern 101 — Adapter Pattern

Phayao Boonon
3 min readAug 18, 2019

--

สำหรับ 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 อย่างไร

  1. Client เรียกใช้งาน Adapter โดย call ไปที่ method ของ target interface ของ Adapter
  2. Adapter จะแปลงสิ่งที่ Client เรียกใช้ไปเป็น หนึ่งหรือมากกว่า call ไปยัง Adaptee ด้วย adaptee interface
  3. 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 ที่ไม่เข้ากัน

https://www.oreilly.com/library/view/head-first-design/0596007124/

สรุป

จากที่ได้ทบทวนและใช้งาน Adapter Pattern จะเห็นได้ว่า pattern นี้มีประโยชน์มากในการแปลงจาก class ที่เรามีอยู่กับ class ใหม่ที่เข้ากันไม่ได้ ด้วยการสร้าง Adapter class เพื่อทำหน้าที่แปลงค่าผ่านทาง interface ซึ่งเป็นรูปแบบของ Object Adapter แต่มี Adapter อีกแบบคือ Class Adapter ที่ไม่ได้ครอบคลุมในบทความนี้ เพราะใช้ multiple inheritance ซึ่ง Java ไม่รองรับ และตัวอย่างก็เอามาจาก Wiki 😅

--

--

Phayao Boonon
Phayao Boonon

Written by Phayao Boonon

Software Engineer 👨🏻‍💻 Stay Hungry Stay Foolish

No responses yet