![Contoh Adapter Design Pattern](https://images.ctfassets.net/7wjlcr9d5ii8/6bvQC5ZTUl0SH4Cddl0hNx/ac927db35331bc98c14f5173fe8544e8/sticker-cartoon-plug-socket-illustrated-146182573.jpg?w=800&h=800&fl=progressive&q=75&fm=jpg)
Adapter Pattern ini memungkinkan objek dengan tipe yang berbeda bentuk dapat digunakan sebagai objek lain. Objek tersebut dihandle agar compatible digunakan sebagai objek lain tanpa harus di-casting atau ditulis ulang. Penasaran kan seperti apa😁?
Adapter Design Pattern adalah struktural design pattern yang dapat membuat objek yang strukturnya tidak saling kompatibel bisa berkolaborasi.
Design Pattern
Use Case
Kita masih menggunakan contoh pada Decorator Pattern sebelumnya tentang struktur senjata. Kalau belum baca, boleh diklik dulu tulisannya biar nyambung dengan contoh pada Adapter Design Pattern di bawah karena pembahasannya berkaitan. Sekarang kita ingin menjadikan objek lain yang bukan senjata, yaitu peralatan rumah tangga, menjadi senjata😎. Kurang lebih seperti berikut requirement-nya:
- Kita membutuhkan abstrak Weapon seperti sebelumnya;
- Kita mempunyai abstrak Home Tools yang mempunyai struktur kekuatan metal pada objek (metal power) dan level kontrol, seberapa ringan objek tersebut digunakan (control level);
- Contoh objek dari HomeTools adalah Sapu (Broom), Panci (Pan), Palu (Hammer), dan barang-barang sejenisnya;
- Objek Home Tools tersebut dapat dijadikan senjata dengan spesifikasi khusus menggunakan algoritma berikut;
- Tingkat akurasi Home Tools adalah sesuai dengan level kontrol objek tersebut;
- Tingkat damage Home Tools adalah 2 kali dari level kekuatan metal pada objek tersebut;
- Tingkat range Home Tools adalah sepertiga dari level kontrol objek tersebut;
- Ukuran kapasitas amunisi Home Tools adalah tak terhingga, karena Home Tools ga membutuhkan peluru;
- Home Tools yang digunakan akan berbunyi keras jika objek tersebut memiliki level kekuatan metal lebih dari 5 points;
Contoh code
Kira-kira bagaimana ya pendekatannya🤔? Mari kita coba menggunakan pendekatan inheritance. Kita akan coba menggunakan objek Broom dengan spesifikasi seperti berikut:
- MetalPower: 2
- ControlLevel: 10
User kemudian menjadikan objek tersebut sebagai Weapon. Maka sesuai requirement di atas, ekspektasi objek Broom tersebut akan menjadi senjata dengan spesifikasi berikut:
- Akurasi: 10
- Damage: 4
- MagazineCapacity: max capacity;
- Range: 5
- Silence: true
Weapon Interface
public interface Weapon{
int getAccuracy();
int getDamage();
int getRange();
int getMagazineCapacity();
boolean isSilence();
}
HomeTools Interface
public interface HomeTools{
int getMetalPower();
int getControlLevel();
}
HomeWeapon Abstract Class
public abstract class HomeWeapon implements Weapon{
@Override
public int getAccuracy(){
return getControlLevel();
}
@Override
public int getDamage(){
return getMetalPower() * 2;
}
@Override
public int getRange(){
return getControlLevel() / 3;
}
@Override
public int getMagazineCapacity(){
return Integer.MAX_VALUE;
}
@Override
public boolean isSilence(){
return getMetalPower() <= 5;
}
}
Broom Class
public class Broom extends HomeWeapon implements HomeTools{
private final int metalPower;
private final int controlLevel;
public Broom(int metalPower, int controlLevel){
this.metalPower = metalPower;
this.controlLevel = controlLevel;
}
@Override
public int getMetalPower(){
return metalPower;
}
@Override
public int getControlLevel(){
return controlLevel;
}
}
Contoh penggunaan
public static void main(String[] args){
HomeTools broom = new Broom(2, 10);
Weapon broomWeapon = (Weapon) broom;
System.out.println("broomWeapon.getAccuracy() = " + broomWeapon.getAccuracy());
System.out.println("broomWeapon.getDamage() = " + broomWeapon.getDamage());
System.out.println("broomWeapon.getMagazineCapacity() = " + broomWeapon.getMagazineCapacity());
System.out.println("broomWeapon.getRange() = " + broomWeapon.getRange());
System.out.println("broomWeapon.isSilence() = " + broomWeapon.isSilence());
}
Masalah
Anggaplah hampir semua HomeTools seperti Broom, Pan, dan sejenisnya punya algoritma seperti di atas. Tapi ketika terjadi perubahan, misalkan ada beberapa spesifik HomeTools yang punya algoritma yang berbeda dengan requirement sebelumnya, seperti Hammer yang level damage-nya akan naik 3 kali lipat dari metal power. Tentu bakal ribet. Super class HomeWeapon akan sulit di-maintain ketika ada spesifikasi khusus seperti itu. Selain itu penggunaan inheritance untuk melakukan reuse code merupakan bad practice, gw juga pernah bikin tulisan itu tentang Composition over Inheritance. Secara tidak langsung setiap objek HomeTools akan selalu membawa struktur HomeWeapon kemanapun dia digunakan, meskipun ketika user hanya butuh struktur HomeTools saja di beberapa kondisi tanpa butuh diubah menjadi Weapon.
Solusi
Untuk class Weapon dan HomeTools tidak ada perubahan. Kita tidak membutuhkan class HomeWeapon lagi seperti sebelumnya, melainkan membuat class baru, yaitu HomeWeaponAdapter. Sekarang lihat perbedaannya ketika kita refactor menggunakan Adapter Design Pattern😎.
HomeWeaponAdapter Class
public class HomeWeaponAdapter implements Weapon{
private final HomeTools homeTools;
public HomeWeaponAdapter(HomeTools homeTools){
this.homeTools = homeTools;
}
@Override
public int getAccuracy(){
return homeTools.getControlLevel();
}
@Override
public int getDamage(){
return homeTools.getMetalPower() * 2;
}
@Override
public int getRange(){
return homeTools.getControlLevel() / 3;
}
@Override
public int getMagazineCapacity(){
return Integer.MAX_VALUE;
}
@Override
public boolean isSilence(){
return homeTools.getMetalPower() <= 5;
}
}
Broom Class
public class Broom implements HomeTools{
private final int metalPower;
private final int controlLevel;
public Broom(int metalPower, int controlLevel){
this.metalPower = metalPower;
this.controlLevel = controlLevel;
}
@Override
public int getMetalPower(){
return metalPower;
}
@Override
public int getControlLevel(){
return controlLevel;
}
}
Contoh penggunaan
public static void main(String[] args){
HomeTools broom = new Broom(2, 10);
Weapon broomWeapon = new HomeWeaponAdapter(broom);
System.out.println("broomWeapon.getAccuracy() = " + broomWeapon.getAccuracy());
System.out.println("broomWeapon.getDamage() = " + broomWeapon.getDamage());
System.out.println("broomWeapon.getMagazineCapacity() = " + broomWeapon.getMagazineCapacity());
System.out.println("broomWeapon.getRange() = " + broomWeapon.getRange());
System.out.println("broomWeapon.isSilence() = " + broomWeapon.isSilence());
}
Nah, dengan menggunakan composition, setiap objek HomeTools tidak harus membawa struktur Weapon seperti pada contoh sebelumnya. Cukup membungkusnya ke dalam objek adapter jika ingin mengubah strukturnya. Setiap ada perubahan algoritma, tinggal bikin Adapter Pattern yang baru tanpa menyenggol bagian apapun pada code sebelumnya.
Kenapa menggunakan Adapater Design Pattern?
Dengan menggunakan Adapter Pattern, dapat membuat maintenance lebih mudah. Dengan menjadikan objek sebagai composition instead of inheritance, dapat membuat struktur data jadi lebih terorganisir. Objek Home Tools lain, seperti Pan atau Hammer yang ingin dijadikan Weapon tinggal dibungkus ke dalam objek HomeWeaponAdapter.
Verdict
Sama seperti Decorator Pattern, Adapter Pattern merupakan structural pattern karena sama-sama mengubah behavior objek dengan membungkusnya ke sebuah objek khusus. Cara pemakaiannya juga mirip. Bedanya, Decorator Pattern mengubahnya untuk tipe yang sama, sedangkan Adapter Pattern mengubahnya menjadi tipe yang berbeda. Gw rasa kita semua sebenarnya pernah atau bahkan sering menggunakan implementasi Adapter Design Pattern ini pada Java, yaitu ketika menggunakan Arrays.asList() method. Itu juga termasuk Adapter Pattern karena membuat struktur Array menjadi compatible sebagai sebuah List, dimana Array dan List merupakan struktur objek yang berbeda.