Saya mengalami kesulitan mencari sumber daya tentang mengapa saya harus menggunakan injeksi ketergantungan . Sebagian besar sumber daya yang saya lihat menjelaskan bahwa itu hanya meneruskan sebuah instance objek ke instance objek lain, tetapi mengapa? Apakah ini hanya untuk arsitektur / kode yang lebih bersih atau apakah ini mempengaruhi kinerja secara keseluruhan?
Mengapa saya harus melakukan yang berikut?
class Profile {
public function deactivateProfile(Setting $setting)
{
$setting->isActive = false;
}
}
Alih-alih berikut ini?
class Profile {
public function deactivateProfile()
{
$setting = new Setting();
$setting->isActive = false;
}
}
deactivateProfile
menyarankan kepada saya bahwa pengaturanisActive
ke false tanpa peduli dengan keadaan sebelumnya adalah pendekatan yang benar di sini. Memanggil metode secara inheren berarti Anda bermaksud mengaturnya sebagai tidak aktif, bukan mendapatkan status aktif saat ini.Jawaban:
Keuntungannya adalah bahwa tanpa injeksi ketergantungan, kelas Profil Anda
Tetapi dengan injeksi ketergantungan
Ini mungkin tampak (atau bahkan) tidak relevan dalam kasus khusus ini, tetapi bayangkan jika kita tidak berbicara tentang objek Pengaturan, tetapi objek DataStore, yang mungkin memiliki implementasi yang berbeda, yang menyimpan data dalam file dan yang lain menyimpannya dalam sebuah database. Dan untuk pengujian otomatis Anda ingin implementasi tiruan juga. Sekarang Anda benar-benar tidak ingin kelas Profile ke hardcode mana yang digunakannya - dan bahkan yang lebih penting, Anda benar-benar tidak ingin kelas Profile tahu tentang jalur sistem file, koneksi DB dan kata sandi, jadi pembuatan objek DataStore harus terjadi di tempat lain.
sumber
This may seem (or even be) irrelevant in this particular case
Saya pikir ini sangat relevan. Bagaimana Anda mendapatkan pengaturan? Banyak sistem yang saya lihat akan memiliki set pengaturan default hard-coded dan konfigurasi publik, jadi Anda harus memuat keduanya dan menimpa beberapa nilai dengan pengaturan publik. Anda bahkan mungkin membutuhkan banyak sumber default. Mungkin Anda bahkan mendapatkan beberapa dari disk, yang lain dari DB. Jadi, seluruh logika bahkan untuk mendapatkan pengaturan dapat, dan sering kali, non-sepele - jelas bukan sesuatu yang mengkonsumsi kode yang harus atau akan dipedulikan.$setting = new Setting();
tidak efisien. Injeksi dan instantiasi objek terjadi sekali.Ketergantungan Injeksi membuat kode Anda lebih mudah untuk diuji.
Saya mempelajari ini secara langsung ketika saya ditugaskan untuk memperbaiki bug yang sulit ditangkap dalam integrasi PayPal Magento.
Suatu masalah akan muncul ketika PayPal memberi tahu Magento tentang pembayaran yang gagal: Magento tidak akan mendaftarkan kegagalan dengan benar.
Menguji potensi perbaikan "secara manual" akan sangat melelahkan: Anda harus entah bagaimana memicu pemberitahuan PayPal "Gagal". Anda harus mengirim cek-e, membatalkannya, dan menunggu sampai kesalahan keluar. Itu berarti 3+ hari untuk menguji perubahan kode satu karakter!
Untungnya, tampaknya para pengembang inti Magento yang mengembangkan fungsi ini memiliki pengujian dalam pikiran, dan menggunakan pola injeksi ketergantungan untuk membuatnya sepele. Ini memungkinkan kami memverifikasi pekerjaan kami dengan test case sederhana seperti ini:
Saya yakin pola DI memiliki banyak keunggulan lain, tetapi peningkatan testabilitas adalah satu-satunya manfaat terbesar dalam pikiran saya .
Jika Anda ingin tahu tentang solusi untuk masalah ini, lihat repo GitHub di sini: https://github.com/bubbleupdev/BUCorefix_Paypalstatus
sumber
Mengapa (apa masalahnya)?
Mnemonik terbaik yang saya temukan untuk ini adalah " baru adalah lem ": Setiap kali Anda menggunakan
new
kode Anda, kode itu terikat ke implementasi spesifik itu . Jika Anda berulang kali menggunakan konstruktor baru, Anda akan membuat rantai implementasi spesifik . Dan karena Anda tidak dapat "memiliki" instance kelas tanpa membangunnya, Anda tidak dapat memisahkan rantai itu.Sebagai contoh, bayangkan Anda sedang menulis video game mobil balap. Anda mulai dengan kelas
Game
, yang menciptakanRaceTrack
, yang menciptakan 8Cars
, yang masing-masing membuatMotor
. Sekarang jika Anda ingin 4 tambahanCars
dengan akselerasi yang berbeda, Anda harus mengubah setiap kelas yang disebutkan , kecuali mungkinGame
.Kode pembersih
Ya .
Namun, mungkin terlihat kurang jelas dalam situasi ini, karena ini lebih merupakan contoh bagaimana melakukannya . Keuntungan sebenarnya hanya menunjukkan ketika beberapa kelas terlibat dan lebih sulit untuk diperagakan, tetapi bayangkan Anda akan menggunakan DI pada contoh sebelumnya. Kode yang menciptakan semua itu mungkin terlihat seperti ini:
Menambahkan 4 mobil yang berbeda sekarang dapat dilakukan dengan menambahkan baris ini:
RaceTrack
,Game
,Car
, atauMotor
yang diperlukan - yang berarti kita bisa 100% yakin bahwa kita tidak memperkenalkan bug baru ada!Pertimbangan kinerja
Tidak ada . Tapi jujur sepenuhnya dengan Anda, mungkin saja.
Namun, bahkan dalam kasus itu, jumlah yang sangat kecil sehingga Anda tidak perlu peduli. Jika suatu saat nanti, Anda harus menulis kode untuk tamagotchi dengan setara dengan 5Mhz CPU dan 2MB RAM, maka mungkin Anda mungkin harus peduli tentang ini.
Dalam 99,999% * kasus, ini akan memiliki kinerja yang lebih baik, karena Anda menghabiskan lebih sedikit waktu untuk memperbaiki bug dan lebih banyak waktu meningkatkan algoritma sumber daya berat Anda.
* Nomor benar-benar dibuat
Info tambahan: "hard-coded"
Jangan salah, ini adalah masih sangat banyak "hard-kode" - angka yang ditulis langsung dalam kode. Bukan hard-coded berarti menyimpan nilai-nilai itu dalam file teks - misalnya dalam format JSON - dan kemudian membacanya dari file itu.
Untuk melakukan itu, Anda harus menambahkan kode untuk membaca file dan kemudian mem-parsing JSON. Jika Anda mempertimbangkan contohnya lagi; dalam versi non-DI, a
Car
atau yangMotor
sekarang harus membaca file. Kedengarannya tidak masuk akal.Dalam versi DI, Anda akan menambahkannya ke kode pengaturan permainan.
sumber
Saya selalu bingung dengan injeksi ketergantungan. Tampaknya hanya ada di lingkungan Jawa, tetapi bola itu berbicara dengan sangat hormat. Itu adalah salah satu Pola besar, Anda tahu, yang dikatakan menertibkan kekacauan. Tetapi contoh-contohnya selalu berbelit-belit dan tiruan, membuat non-masalah dan kemudian berangkat untuk menyelesaikannya dengan membuat kode lebih rumit.
Itu lebih masuk akal ketika sesama Python dev memberi saya kebijaksanaan ini: itu hanya melewati argumen ke fungsi. Sama sekali bukan pola; lebih seperti pengingat bahwa Anda dapat meminta sesuatu sebagai argumen, bahkan jika Anda bisa saja memberikan nilai yang masuk akal sendiri.
Jadi pertanyaan Anda kira-kira setara dengan "mengapa fungsi saya harus mengambil argumen?" dan memiliki banyak jawaban yang sama, yaitu: membiarkan penelepon membuat keputusan .
Ini tentu saja memerlukan biaya, karena sekarang Anda memaksa pemanggil untuk membuat beberapa keputusan (kecuali jika Anda membuat argumen opsional), dan antarmuka agak rumit. Sebagai gantinya, Anda mendapatkan fleksibilitas.
Jadi: Apakah ada alasan bagus mengapa Anda perlu menggunakan
Setting
jenis / nilai khusus ini? Apakah ada alasan yang baik memanggil kode mungkin menginginkanSetting
jenis / nilai yang berbeda ? (Ingat, tes adalah kode !)sumber
Contoh yang Anda berikan bukanlah injeksi ketergantungan dalam arti klasik. Ketergantungan injeksi biasanya mengacu pada objek yang lewat dalam konstruktor atau dengan menggunakan "setter injection" tepat setelah objek dibuat, untuk menetapkan nilai pada bidang dalam objek yang baru dibuat.
Contoh Anda meneruskan objek sebagai argumen ke metode instance. Metode instance ini kemudian memodifikasi bidang pada objek itu. Ketergantungan injeksi? Tidak. Memecah enkapsulasi dan menyembunyikan data? Benar!
Sekarang, jika kodenya seperti ini:
Maka saya akan mengatakan Anda menggunakan injeksi ketergantungan. Perbedaan penting adalah
Settings
objek yang diteruskan ke konstruktor atauProfile
objek.Ini berguna jika objek Pengaturan mahal atau kompleks untuk dibangun, atau Pengaturan adalah antarmuka atau kelas abstrak di mana ada beberapa implementasi konkret untuk mengubah perilaku run time.
Karena Anda secara langsung mengakses bidang pada objek Pengaturan daripada memanggil metode, Anda tidak dapat memanfaatkan Polimorfisme, yang merupakan salah satu manfaat injeksi ketergantungan.
Sepertinya Pengaturan untuk Profil khusus untuk profil itu. Dalam hal ini saya akan melakukan salah satu dari yang berikut:
Instantiate objek Pengaturan di dalam konstruktor Profil
Lewati objek Pengaturan di konstruktor dan salin bidang individual yang berlaku untuk Profil
Jujur, dengan meneruskan objek Pengaturan ke
deactivateProfile
dan kemudian memodifikasi bidang internal objek Pengaturan adalah bau kode. Objek Pengaturan harus menjadi satu-satunya yang memodifikasi bidang internal.sumber
The example you give is not dependency injection in the classical sense.
- Tidak masalah. OO orang memiliki benda di otak, tetapi Anda masih menyerahkan ketergantungan pada sesuatu.Saya tahu saya datang terlambat ke pesta ini tapi saya merasa ada poin penting yang terlewatkan.
Anda seharusnya tidak. Tetapi bukan karena Ketergantungan Injeksi adalah ide yang buruk. Itu karena ini salah.
Mari kita lihat hal-hal ini menggunakan kode. Kami akan melakukan ini:
ketika kita mendapatkan hal yang sama dari ini:
Jadi tentu saja sepertinya buang-buang waktu saja. Itu adalah ketika Anda melakukannya dengan cara ini. Ini bukan penggunaan Dependency Injection terbaik. Ini bahkan bukan penggunaan terbaik dari sebuah kelas.
Sekarang bagaimana jika sebaliknya kita punya ini:
Dan sekarang
application
bebas untuk mengaktifkan dan menonaktifkanprofile
tanpa harus tahu apa-apa khususnya tentangsetting
itu benar-benar berubah. Kenapa itu bagus? Jika Anda perlu mengubah pengaturan. Theapplication
berdinding off dari perubahan tersebut sehingga Anda bebas untuk pergi kacang dalam ruang yang terkandung aman tanpa harus melihat segala sesuatu memecah segera setelah Anda menyentuh sesuatu.Ini mengikuti konstruksi terpisah dari prinsip perilaku . Pola DI di sini sederhana. Bangun semua yang Anda butuhkan pada level serendah yang Anda bisa, sambungkan semuanya, lalu mulailah semua perilaku berdetak dengan satu panggilan.
Hasilnya adalah Anda memiliki tempat terpisah untuk memutuskan apa yang terhubung dengan apa dan tempat berbeda untuk mengelola apa yang mengatakan apa untuk apa pun.
Cobalah itu pada sesuatu yang harus Anda pertahankan dari waktu ke waktu dan lihat apakah itu tidak membantu.
sumber
Sebagai pelanggan, ketika Anda menyewa seorang mekanik untuk melakukan sesuatu pada mobil Anda, apakah Anda mengharapkan mekanik tersebut untuk membuat mobil dari awal hanya untuk kemudian bekerja dengannya? Tidak, Anda memberi mekanik mobil yang Anda inginkan untuk mereka kerjakan .
Sebagai pemilik garasi, ketika Anda memerintahkan mekanik untuk melakukan sesuatu pada mobil, apakah Anda mengharapkan mekanik untuk membuat sendiri obeng / kunci pas / suku cadang mobil? Tidak, Anda memberi mekanik bagian / alat yang ia butuhkan
Kenapa kita melakukan ini? Nah, pikirkan tentang itu. Anda adalah pemilik garasi yang ingin mempekerjakan seseorang untuk menjadi mekanik Anda. Anda akan mengajari mereka menjadi mekanik (= Anda akan menulis kode).
Apa yang akan lebih mudah:
Ada manfaat besar jika mekanik Anda tidak membuat semuanya dari awal:
Jika Anda merekrut dan melatih mekanik ahli, Anda akan berakhir dengan seorang karyawan yang membutuhkan biaya lebih banyak, membutuhkan lebih banyak waktu untuk melakukan apa yang seharusnya menjadi pekerjaan sederhana, dan akan perlu dilatih ulang setiap kali salah satu dari banyak tanggung jawab mereka membutuhkan untuk diperbarui.
Analogi pengembangannya adalah bahwa jika Anda menggunakan kelas dengan dependensi hardcod, maka Anda akan berakhir dengan kelas yang sulit dipertahankan yang akan membutuhkan pengembangan / perubahan terus menerus setiap kali versi objek yang baru (
Settings
dalam kasus Anda) dibuat, dan Anda Harus mengembangkan logika internal agar kelas memiliki kemampuan untuk membuat berbagai jenisSettings
objek.Selain itu, siapa pun yang mengkonsumsi kelas Anda sekarang juga harus meminta kelas untuk membuat
Settings
objek yang benar , bukan hanya untuk dapat lulus kelasSettings
objek apa pun yang ingin dilewatinya. Ini berarti pengembangan tambahan bagi konsumen untuk mencari tahu bagaimana cara meminta kelas untuk membuat alat yang tepat.Ya, inversi dependensi membutuhkan sedikit usaha untuk menulis alih-alih hardcoding dependensi. Ya, menyebalkan harus mengetik lebih banyak.
Tapi itu argumen yang sama seperti memilih ke nilai literal hardcode karena "mendeklarasikan variabel membutuhkan lebih banyak usaha". Secara teknis benar, tetapi pro lebih besar daripada kontra dengan beberapa urutan besarnya .
Manfaat inversi ketergantungan tidak dialami ketika Anda membuat versi pertama aplikasi. Manfaat inversi ketergantungan dialami ketika Anda perlu mengubah atau memperluas versi awal itu. Dan jangan menipu diri sendiri dengan berpikir bahwa Anda akan melakukannya dengan benar pertama kali dan tidak perlu memperpanjang / mengubah kode. Anda harus mengubah banyak hal.
Ini tidak mempengaruhi kinerja runtime aplikasi. Tapi itu secara besar-besaran berdampak pada waktu pengembangan (dan karenanya kinerja) pengembang .
sumber
Seperti semua pola, sangat valid untuk bertanya "mengapa" untuk menghindari desain kembung.
Untuk injeksi ketergantungan, ini mudah dilihat dengan memikirkan dua, yang paling penting, aspek terpenting dari desain OOP ...
Kopling rendah
Kopling dalam pemrograman komputer :
Anda ingin mencapai kopling rendah . Dua hal yang sangat berpasangan berarti bahwa jika Anda mengubah yang satu, kemungkinan besar Anda harus mengubah yang lain. Bug atau batasan dalam satu kemungkinan akan menyebabkan bug / pembatasan di yang lain; dan seterusnya.
Satu kelas objek instantiating yang lain adalah kopling yang sangat kuat, karena yang satu perlu tahu tentang keberadaan yang lain; ia perlu tahu bagaimana membuat instantiate (argumen mana yang dibutuhkan oleh konstruktor), dan argumen itu harus tersedia saat memanggil konstruktor. Juga, tergantung pada apakah bahasa tersebut membutuhkan dekonstruksi eksplisit (C ++), ini akan menimbulkan komplikasi lebih lanjut. Jika Anda memperkenalkan kelas baru (yaitu, a
NextSettings
atau apa pun), Anda harus kembali ke kelas asli dan menambahkan lebih banyak panggilan ke konstruktor mereka.Kohesi tinggi
Kohesi :
Ini adalah sisi lain dari koin. Jika Anda melihat satu unit kode (satu metode, satu kelas, satu paket dll), Anda ingin memiliki semua kode di dalam unit itu untuk memiliki tanggung jawab sesedikit mungkin.
Contoh dasar untuk ini adalah pola MVC: Anda memisahkan dengan jelas model domain dari view (GUI) dan lapisan kontrol yang menempelkan keduanya.
Ini menghindari kode mengasapi di mana Anda mendapatkan potongan besar yang melakukan banyak hal berbeda; jika Anda ingin mengubah beberapa bagian dari itu, Anda harus melacak semua fitur lainnya juga, berusaha menghindari bug, dll .; dan Anda dengan cepat memprogram diri Anda ke dalam lubang di mana sangat sulit untuk keluar.
Dengan injeksi dependensi, Anda mendelegasikan pembuatan atau melacak, baik, dependensi ke kelas apa pun (atau file konfigurasi) yang mengimplementasikan DI Anda. Kelas-kelas lain tidak akan terlalu peduli tentang apa yang sebenarnya terjadi - mereka akan bekerja dengan beberapa antarmuka generik, dan tidak tahu apa implementasi yang sebenarnya, yang berarti mereka tidak bertanggung jawab atas hal-hal lain.
sumber
Anda harus menggunakan teknik untuk memecahkan masalah yang mereka pecahkan saat Anda memiliki masalah tersebut. Ketergantungan inversi dan injeksi tidak berbeda.
Inversi ketergantungan atau injeksi adalah teknik yang memungkinkan kode Anda untuk memutuskan implementasi metode apa yang dipanggil saat dijalankan. Ini memaksimalkan manfaat dari ikatan yang terlambat. Teknik ini diperlukan ketika bahasa tidak mendukung penggantian fungsi non-instance waktu berjalan. Misalnya, Java tidak memiliki mekanisme untuk mengganti panggilan ke metode statis dengan panggilan ke implementasi yang berbeda; kontras dengan Python, di mana semua yang diperlukan untuk mengganti pemanggilan fungsi adalah untuk mengikat nama ke fungsi yang berbeda (menugaskan kembali variabel yang memegang fungsi).
Mengapa kita ingin memvariasikan implementasi fungsi? Ada dua alasan utama:
Anda mungkin juga ingin mencatat inversi wadah kontrol. Ini adalah teknik yang dimaksudkan untuk membantu Anda menghindari pohon konstruksi besar yang kusut yang terlihat seperti kodesemu ini:
Ini memungkinkan Anda mendaftarkan kelas Anda dan kemudian melakukan konstruksi untuk Anda:
Perhatikan bahwa ini paling sederhana jika kelas yang didaftarkan dapat menjadi lajang tanpa kewarganegaraan .
Kata hati-hati
Perhatikan bahwa inversi dependensi seharusnya bukan jawaban masuk Anda untuk logika decoupling. Cari peluang untuk menggunakan parameterisasi sebagai gantinya. Pertimbangkan metode pseudocode ini misalnya:
Kami dapat menggunakan inversi ketergantungan untuk beberapa bagian dari metode ini:
Tetapi kita seharusnya tidak, setidaknya tidak sepenuhnya. Perhatikan bahwa kami telah membuat kelas stateful dengan
Querier
. Sekarang memegang referensi ke beberapa objek koneksi dasarnya global. Ini menciptakan masalah seperti kesulitan dalam memahami keadaan keseluruhan program dan bagaimana kelas yang berbeda berkoordinasi satu sama lain. Perhatikan juga bahwa kita terpaksa memalsukan querier atau koneksi jika kita ingin menguji logika rata-rata. Lebih lanjut Pendekatan yang lebih baik adalah meningkatkan parameterisasi :Dan koneksi akan dikelola pada tingkat yang lebih tinggi yang bertanggung jawab untuk operasi secara keseluruhan dan tahu apa yang harus dilakukan dengan output ini.
Sekarang kita dapat menguji logika rata-rata sepenuhnya terlepas dari kueri, dan terlebih lagi kita dapat menggunakannya dalam berbagai situasi yang lebih luas. Kita mungkin mempertanyakan apakah kita bahkan membutuhkan
MyQuerier
danAverager
objek, dan mungkin jawabannya adalah kita tidak melakukannya jika kita tidak berniat untuk pengujian unitStuffDoer
, dan bukan pengujian unitStuffDoer
akan sangat masuk akal karena begitu erat dengan database. Mungkin lebih masuk akal untuk membiarkan tes integrasi menutupinya. Dalam hal ini, kita mungkin bisa membuatfetchAboveMin
danaverageData
menjadi metode statis.sumber