Adapter Pattern #

Dalam pengembangan software, kita sering dihadapkan pada situasi di mana sebuah komponen atau library yang sudah ada tidak memiliki interface yang sesuai dengan yang dibutuhkan oleh sistem kita. Mengubah kode lama sering kali tidak memungkinkan atau tidak diinginkan (misalnya karena kode tersebut berasal dari third-party library atau sudah stabil di production).

Di sinilah Adapter Pattern berperan. Pattern ini membantu kita menjembatani ketidakcocokan interface tanpa harus mengubah implementasi asli.

Adapter Pattern termasuk dalam kategori Structural Design Pattern.

Apa itu Adapter Pattern? #

Adapter Pattern adalah design pattern yang memungkinkan dua interface yang tidak kompatibel untuk dapat bekerja bersama dengan cara menambahkan lapisan adapter.

Adapter bertindak sebagai penerjemah antara:

  • Interface yang dibutuhkan oleh client
  • Interface yang disediakan oleh objek yang sudah ada (adaptee)

Secara sederhana:

Client berbicara dengan Adapter, Adapter menerjemahkan ke bahasa yang dimengerti oleh Adaptee.


Tujuan Pattern #

Adapter Pattern bertujuan untuk:

  1. Mengisolasi ketergantungan terhadap implementasi konkret
  2. Menghindari perubahan pada kode existing
  3. Meningkatkan reusability dari komponen lama
  4. Menjaga Single Responsibility Principle (logika adaptasi tidak bercampur dengan bisnis utama)

Kapan Cocok Digunakan? #

Gunakan Adapter Pattern ketika:

  • Kamu ingin menggunakan library / service existing tetapi interfacenya berbeda
  • Kamu sedang melakukan migrasi sistem secara bertahap
  • Kamu ingin menyamakan interface dari beberapa implementasi yang berbeda
  • Kamu ingin menambahkan lapisan kompatibilitas tanpa refactor besar

Contoh kasus nyata:

  • Integrasi payment gateway dengan API berbeda-beda
  • Wrapper client HTTP / SDK pihak ketiga
  • Menghubungkan legacy system dengan kode baru

Struktur Pattern #

Komponen utama:

  1. Target → interface yang diharapkan oleh client
  2. Adaptee → objek lama dengan interface berbeda
  3. Adapter → menjembatani Target dan Adaptee
  4. Client → menggunakan Target

Contoh Implementasi (Golang) #

Studi Kasus #

Misalnya sistem kita membutuhkan interface PaymentProcessor, tetapi kita memiliki library lama LegacyPaymentGateway dengan method yang berbeda.

Target Interface #

package payment

// Interface yang diharapkan oleh sistem
// Target

type PaymentProcessor interface {
	Pay(amount int) error
}

Adaptee (Legacy Code) #

package legacy

// Kode lama / third-party library
// Adaptee

type LegacyPaymentGateway struct {}

func (l *LegacyPaymentGateway) MakePayment(total int) error {
	// logic pembayaran lama
	return nil
}

Adapter #

package adapter

import (
	"myapp/payment"
	"myapp/legacy"
)

// Adapter mengimplementasikan PaymentProcessor
// dan membungkus LegacyPaymentGateway

type LegacyPaymentAdapter struct {
	gateway *legacy.LegacyPaymentGateway
}

func NewLegacyPaymentAdapter(gateway *legacy.LegacyPaymentGateway) payment.PaymentProcessor {
	return &LegacyPaymentAdapter{gateway: gateway}
}

func (a *LegacyPaymentAdapter) Pay(amount int) error {
	// Translasi method
	return a.gateway.MakePayment(amount)
}

Client Code #

package main

import (
	"myapp/adapter"
	"myapp/legacy"
)

func main() {
	legacyGateway := &legacy.LegacyPaymentGateway{}
	processor := adapter.NewLegacyPaymentAdapter(legacyGateway)

	_ = processor.Pay(100000)
}

Client tidak tahu dan tidak peduli bahwa yang digunakan adalah legacy gateway.


Object Adapter vs Class Adapter #

Dalam praktik:

  • Object Adapter (composition) → seperti contoh di atas
  • Class Adapter (inheritance) → tidak relevan di Go karena Go tidak mendukung inheritance klasik

➡️ Golang secara natural mendorong Object Adapter.


Kelebihan dan Kekurangan #

✅ Kelebihan #

  • Tidak mengubah kode existing
  • Mendukung Open/Closed Principle
  • Mudah untuk testing dan mocking

❌ Kekurangan #

  • Menambah jumlah layer
  • Bisa membingungkan jika terlalu banyak adapter

Hubungan dengan Pattern Lain #

  • Facade → menyederhanakan interface, bukan mengadaptasi
  • Decorator → menambah behavior, bukan menerjemahkan interface
  • Bridge → memisahkan abstraksi dan implementasi sejak awal

Best Practices #

Jangan Memasukkan Business Logic ke Adapter #

Adapter seharusnya hanya:

  • Mapping method
  • Konversi parameter
  • Penyesuaian format data

Jika logic mulai kompleks, itu tanda:

  • Pattern lain mungkin lebih cocok

Letakkan Adapter di Layer Infrastruktur #

Biasanya adapter cocok ditempatkan di:

  • infrastructure
  • integration
  • external

Ini menjaga domain tetap bersih.

Gunakan Interface sebagai Kontrak #

Client harus bergantung pada interface, bukan adapter konkret.

func ProcessPayment(p payment.PaymentProcessor) {
	_ = p.Pay(50000)
}

Satu Adapter untuk Satu Adaptee #

Hindari adapter yang menangani banyak implementasi sekaligus. Jika perlu, buat adapter terpisah.

Jangan Overuse Adapter Pattern #

Jika kamu mengontrol kedua sisi kode, lebih baik:

  • Refactor interface langsung
  • Hindari adapter yang tidak perlu

Penutup #

Adapter Pattern adalah solusi elegan untuk mengatasi ketidakcocokan interface tanpa merusak kode lama. Di Golang, pattern ini sangat natural karena:

  • Interface bersifat implisit
  • Composition lebih diutamakan daripada inheritance

Gunakan Adapter Pattern saat integrasi eksternal, migrasi sistem, atau menjaga boundary antar layer tetap bersih.

Jika digunakan dengan tepat, Adapter Pattern dapat membuat sistem lebih fleksibel, maintainable, dan scalable.

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact