![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=100&fm=jpg&fit=thumb&f=faces)
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 Structural Design Pattern yang dapat membuat objek yang tipe strukturnya tidak saling kompatibel bisa berkolaborasi.
Design Pattern
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;
Kita akan menjadikan alat-alat rumah tangga sebagai senjata seperti Sapu, Panci, Palu, dll. Akan tetapi, struktur data senjata dan alat rumah tangga berbeda, senjata memiliki struktur akurasi, damage, magazine capacity, range dan silenced. Sedangkan alat rumah tangga hanya memiliki struktur Metal Power dan Control Level. Kira-kira bagaimana ya pendekatannya agar alat rumah tangga bisa digunakan sebagai senjata🤔? Pertama, mari kita coba menggunakan pendekatan inheritance. Kita akan 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;
print(broomWeapon);
}
public static void print(Weapon weapon){
System.out.println("weapon.getAccuracy() = " + weapon.getAccuracy());
System.out.println("weapon.getDamage() = " + weapon.getDamage());
System.out.println("weapon.getMagazineCapacity() = " + weapon.getMagazineCapacity());
System.out.println("weapon.getRange() = " + weapon.getRange());
System.out.println("weapon.isSilence() = " + weapon.isSilence());
}
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 karena dependency-nya jadi ga keliatan. Lalu akan terjadi perubahan di struktur datanya karena kita harus mengganti super class dari Hammer. 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.
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);
print(broomWeapon);
}
public static void print(Weapon weapon){
System.out.println("weapon.getAccuracy() = " + weapon.getAccuracy());
System.out.println("weapon.getDamage() = " + weapon.getDamage());
System.out.println("weapon.getMagazineCapacity() = " + weapon.getMagazineCapacity());
System.out.println("weapon.getRange() = " + weapon.getRange());
System.out.println("weapon.isSilence() = " + weapon.isSilence());
}
Nah, dengan menggunakan composition, setiap objek HomeTools tidak harus membawa struktur Weapon seperti pada contoh sebelumnya. Ketika kita punya objek HomeTools dan ingin menggunakan method print(), tapi methodnya membutuhkan parameter Weapon, maka kita cukup membungkus objek HomeTools pakai HomeWeaponAdapter. Setiap ada perubahan algoritma, tinggal bikin Adapter baru tanpa menyenggol struktur data apapun pada code sebelumnya.
Adapter Pattern dapat membuat maintenance lebih mudah tanpa banyak casting objek. 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. Ketika ada custom adapter baru, tinggal bikin class adapter yang baru tanpa mengubah code yang sudah ada.
Fungsi objek Adapter itu persis seperti ketika kita ingin mencolokkan colokan produk elektronik dari luar negeri yang memiliki 3 jarum ke lubang colokan yang digunakan di Indonesia yang hanya memiliki 2 lubang. Otomatis kita harus beli colokan Adapter agar bisa membuat colokan 3 jarum dari luar negeri tadi bisa digunakan di lubang colokan di Indonesia tanpa harus mengganti colokan. 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 menggunakan Dependency Injection Principle. 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. Contoh lainnya adalah Collections.singletonList() dan Collections.singleton() pada Java ketika ingin menggunakan single object sebagai immutable List dan Set.