Injeksi Dependensi
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GO di Coddy. Pelajaran 45 dari 107.
Dependency Injection adalah teknik ketika sebuah struct menerima dependensinya dari luar, bukan membuatnya sendiri. Di Go, interface membuat pola ini terasa alami dan ampuh.
Alih-alih menulis implementasi tertentu secara langsung di dalam struct, kamu menerima interface. Ini memungkinkan kamu mengganti implementasi tanpa mengubah kode struct:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
type SMSNotifier struct{}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
Sekarang buat struct yang bergantung pada interface, bukan tipe konkret:
type OrderService struct {
notifier Notifier // dependensi disuntikkan melalui interface
}
func NewOrderService(n Notifier) *OrderService {
return &OrderService{notifier: n}
}
func (o *OrderService) PlaceOrder(item string) string {
return o.notifier.Send("Order placed: " + item)
}
OrderService tidak tahu atau peduli apakah layanan itu menggunakan email atau SMS. Kamu menyuntikkan dependensi saat membuat layanan:
func main() {
emailService := NewOrderService(EmailNotifier{})
fmt.Println(emailService.PlaceOrder("Book")) // Email: Pesanan dibuat: Book
smsService := NewOrderService(SMSNotifier{})
fmt.Println(smsService.PlaceOrder("Phone")) // SMS: Pesanan dibuat: Phone
}
Pendekatan ini membuat kodemu lebih fleksibel dan mudah diuji. Saat pengujian, kamu bisa memasukkan notifier tiruan yang tidak benar-benar mengirim pesan. Dalam lingkungan produksi, kamu memasukkan implementasi yang sebenarnya. Struct tetap sama dalam kedua skenario.
Tantangan
MudahMari buat sistem pencatatan log yang menunjukkan keunggulan injeksi dependensi. Kamu akan membuat service yang dapat menulis log ke berbagai tujuan, tanpa service tersebut mengetahui atau peduli ke mana log itu sebenarnya dikirim.
Kamu akan menyusun kodemu dalam tiga file:
logger.go: Definisikan interfaceLoggeryang mewajibkan methodLog(message string) string. Lalu buat dua implementasi logger yang berbeda:ConsoleLoggerdengan fieldPrefix. MethodLog-nya mengembalikan[CONSOLE] [Prefix]: [message]FileLoggerdengan fieldFilename. MethodLog-nya mengembalikan[FILE:[Filename]] [message]
service.go: Buat structAppServiceyang bergantung pada interfaceLogger, bukan tipe konkret. Sertakan fungsi konstruktorNewAppServiceyang menerimaLoggerdan mengembalikan pointer keAppService. Tambahkan method bernamaDoWork(task string) stringkeAppServiceyang menggunakan logger yang diinjeksi untuk mencatat pesanProcessing: [task]dan mengembalikan apa pun yang dikembalikan logger.main.go: Baca konfigurasi dari input, buat kedua tipe logger, injeksikan masing-masing ke instanceAppServiceyang terpisah, lalu panggilDoWorkpada setiap service dengan task yang diberikan. Cetak hasil dari setiap pemanggilan service.
Input berikut akan diberikan:
- Baris 1: Prefix logger konsol
- Baris 2: Nama file logger file
- Baris 3: Task yang akan diproses
Contohnya, jika diberikan INFO, app.log, dan user authentication, outputmu seharusnya:
[CONSOLE] INFO: Processing: user authentication
[FILE:app.log] Processing: user authenticationPerhatikan bahwa AppService tidak tahu apakah log dicatat ke konsol atau file. Service itu hanya memanggil Log pada logger mana pun yang diinjeksi. Fleksibilitas ini adalah inti dari injeksi dependensi: kode service yang sama dapat digunakan dengan implementasi pencatatan log yang sepenuhnya berbeda.
Coba sendiri
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Baca prefiks logger konsol
prefix, _ := reader.ReadString('\n')
prefix = prefix[:len(prefix)-1]
// Baca nama file untuk logger file
filename, _ := reader.ReadString('\n')
filename = filename[:len(filename)-1]
// Baca tugas yang akan diproses
task, _ := reader.ReadString('\n')
if len(task) > 0 && task[len(task)-1] == '\n' {
task = task[:len(task)-1]
}
// TODO: Buat ConsoleLogger dengan prefiks tersebut
// TODO: Buat FileLogger dengan nama file tersebut
// TODO: Buat AppService dengan ConsoleLogger yang disuntikkan
// TODO: Buat AppService lain dengan FileLogger yang disuntikkan
// TODO: Panggil DoWork pada setiap layanan dengan tugas tersebut, lalu cetak hasilnya
fmt.Println("result1")
fmt.Println("result2")
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1Dasar-Dasar OOP di Go
File EksternalWorkspace Go dan modulePaket dan ImporNama yang Diekspor dan Tidak DieksporPengantar OOP di Gostruct sebagai classMendefinisikan method pada structPointer receiver vs value receiverInisialisasi structFungsi KonstruktorUlasan: Kalkulator Sederhana4Interface
Pengenalan InterfaceImplementasi ImplisitInterface sebagai KontrakInterface Kosong (any)Type AssertionType SwitchKomposisi InterfaceInterface Stringer & ErrorUlasan: Kalkulator Bentuk7Enkapsulasi
Field yang Diekspor vs Tidak DieksporEnkapsulasi Tingkat PackageMethod Getter dan SetterPenyembunyian Informasi di GoRingkasan: Data Siswa10Generics (Go 1.18+)
Pengantar GenericsParameter TipeBatasan TipeStruct GenerikCara Alternatif untuk Method GenerikRingkasan Koleksi Generik2Mendalami Tipe dan Struct
Tipe Dasar dan KompositDefinisi Tipe KustomTag StructStruct AnonimStruct BersarangNilai Nol dan Nilai BawaanRangkuman: Buku Kontak5Komposisi daripada Pewarisan
Mengapa Go Tidak Memiliki PewarisanDasar Penyematan StructPromosi methodMenyematkan Beberapa StructPenyematan vs AgregasiMembayangi method yang DisematkanRangkuman: Hierarki Karyawan8Penanganan Error & OOP
Interface errorTipe Error KustomPembungkusan Error (fmt.Errorf)Error Sentinelerrors.Is() dan errors.As()Panic, Defer, dan RecoverRangkuman: Parser File3Pointer dan Memori
Dasar-Dasar Pointer di GoPointer ke StructPass by Value vs ReferenceFungsi new()Garbage Collection di GoRekap: Pembuat Linked List6Polimorfisme di Go
Polimorfisme melalui InterfaceDuck Typing di GoAturan Pemenuhan InterfaceKoleksi PolimorfikInjeksi DependensiRangkuman: Pemroses Pembayaran9Konkruensi & OOP
Dasar-dasar GoroutineChannel & KomunikasiChannel Ber-buffer vs Tanpa BufferPernyataan selectsync.Mutex & sync.RWMutexsync.WaitGroupDesain Struct yang Aman untuk ThreadRingkasan: Worker PoolLatihan sendiri: Compiler Go online