Saya mengerti bahwa secara langsung instantiasi dependensi di dalam kelas dianggap praktik buruk. Ini masuk akal karena melakukan hal tersebut dengan ketat memadukan segala sesuatu yang pada gilirannya membuat pengujian menjadi sangat sulit.
Hampir semua kerangka kerja yang saya temui tampaknya mendukung injeksi ketergantungan dengan wadah daripada menggunakan pencari layanan. Keduanya tampaknya mencapai hal yang sama dengan memungkinkan programmer untuk menentukan objek apa yang harus dikembalikan ketika kelas membutuhkan ketergantungan.
Apa perbedaan keduanya? Mengapa saya memilih satu dari yang lain?
dependency-injection
ioc-containers
service-locator
tom6025222
sumber
sumber
Jawaban:
Ketika objek itu sendiri bertanggung jawab untuk meminta dependensinya, sebagai lawan menerima mereka melalui konstruktor, itu menyembunyikan beberapa informasi penting. Ini hanya sedikit lebih baik daripada kasus penggunaan yang sangat ketat
new
untuk instantiate dependensinya. Ini mengurangi kopling karena Anda sebenarnya bisa mengubah dependensi yang didapatnya, tetapi ia masih memiliki dependensi yang tidak dapat diguncang: pencari layanan. Itu menjadi hal yang semuanya bergantung.Wadah yang memasok dependensi melalui argumen konstruktor memberikan kejelasan paling banyak. Kita melihat di depan bahwa suatu objek membutuhkan
AccountRepository
, dan aPasswordStrengthEvaluator
. Saat menggunakan pelacak layanan, informasi itu tidak segera terlihat. Anda akan segera melihat sebuah kasus di mana sebuah objek memiliki, oh, 17 dependensi, dan berkata pada diri sendiri, "Hmm, itu sepertinya sangat banyak. Apa yang terjadi di sana?" Panggilan ke pelacak layanan dapat disebarkan di sekitar berbagai metode, dan bersembunyi di balik logika bersyarat, dan Anda mungkin tidak menyadari bahwa Anda telah menciptakan "kelas Tuhan" - yang melakukan segalanya. Mungkin kelas itu dapat di refactored menjadi 3 kelas kecil yang lebih fokus, dan karenanya lebih dapat diuji.Sekarang pertimbangkan pengujian. Jika suatu objek menggunakan pelacak layanan untuk mendapatkan dependensinya, kerangka kerja pengujian Anda juga akan membutuhkan pelacak layanan. Dalam pengujian, Anda akan mengonfigurasi pelacak layanan untuk memasok dependensi ke objek yang diuji - mungkin a
FakeAccountRepository
dan aVeryForgivingPasswordStrengthEvaluator
, dan kemudian menjalankan tes. Tapi itu lebih banyak pekerjaan daripada menentukan ketergantungan pada konstruktor objek. Dan kerangka kerja pengujian Anda juga menjadi tergantung pada lokasi layanan. Ini hal lain yang harus Anda konfigurasi di setiap tes, yang membuat tes menulis kurang menarik.Cari "Serivce Locator adalah Anti-Pola" untuk artikel Mark Seeman tentang itu. Jika Anda berada di dunia .Net, dapatkan bukunya. Ini sangat bagus.
sumber
constructor supplied dependencies
vsservice locator
adalah bahwa yang pertama dapat diverifikasi waktu kompilasi, sedangkan yang terakhir hanya dapat diverifikasi runtime.But that's more work than specifying dependencies in the object's constructor.
Saya ingin mengajukan keberatan. Dengan pencari lokasi layanan, Anda hanya perlu menentukan 3 dependensi yang sebenarnya Anda perlukan untuk pengujian Anda. Dengan DI berbasis konstruktor Anda perlu menentukan ALL 10 dari mereka, bahkan jika 7 tidak digunakan.Bayangkan Anda adalah seorang pekerja di sebuah pabrik yang membuat sepatu .
Anda bertanggung jawab untuk merakit sepatu dan karenanya Anda akan membutuhkan banyak hal untuk melakukannya.
Dan seterusnya.
Anda sedang bekerja di pabrik dan Anda siap untuk memulai. Anda memiliki daftar instruksi tentang cara melanjutkan, tetapi Anda belum memiliki bahan atau alat apa pun.
Sebuah Layanan Locator seperti Foreman yang dapat membantu Anda mendapatkan apa yang Anda butuhkan.
Anda meminta Service Locator setiap kali Anda membutuhkan sesuatu, dan mereka pergi untuk mencarikannya untuk Anda. Service Locator telah diberi tahu sebelumnya tentang apa yang mungkin Anda tanyakan dan bagaimana menemukannya.
Anda sebaiknya berharap bahwa Anda tidak meminta sesuatu yang tidak terduga. Jika Locator belum diberitahu sebelumnya tentang alat atau bahan tertentu, mereka tidak akan bisa mendapatkannya untuk Anda, dan mereka hanya akan mengangkat bahu kepada Anda.
Sebuah Dependency Injection (DI) Wadah seperti sebuah kotak besar yang akan diisi dengan segala sesuatu yang setiap orang perlu di awal hari.
Saat pabrik dimulai, Bos Besar yang dikenal sebagai Root Komposisi mengambil wadah dan membagikan semuanya kepada Manajer Lini .
Manajer Lini sekarang memiliki apa yang mereka butuhkan untuk melakukan tugas mereka hari itu. Mereka mengambil apa yang mereka miliki dan memberikan apa yang diperlukan kepada bawahan mereka.
Proses ini berlanjut, dengan ketergantungan mengaliri lini produksi. Akhirnya, sebuah wadah material dan peralatan muncul untuk Mandor Anda.
Mandor Anda sekarang mendistribusikan apa yang Anda butuhkan untuk Anda dan pekerja lain, tanpa Anda bahkan meminta mereka.
Pada dasarnya, segera setelah Anda muncul untuk bekerja, semua yang Anda butuhkan sudah ada di dalam kotak menunggu Anda. Anda tidak perlu tahu apa-apa tentang cara mendapatkannya.
sumber
Beberapa poin tambahan yang saya temukan ketika menjelajahi web:
sumber
Saya datang terlambat ke pesta ini tapi saya tidak bisa menolak.
Terkadang tidak ada sama sekali. Yang membuat perbedaan adalah apa yang tahu tentang apa.
Anda tahu Anda menggunakan pencari lokasi layanan ketika klien mencari ketergantungan tahu tentang wadah. Seorang klien yang mengetahui bagaimana menemukan ketergantungannya, bahkan ketika melalui sebuah wadah untuk mendapatkannya, adalah pola pencari lokasi layanan.
Apakah ini berarti jika Anda ingin menghindari pelacak layanan Anda tidak dapat menggunakan wadah? Tidak. Anda hanya perlu menjaga klien agar tidak mengetahui tentang wadah. Perbedaan utama adalah di mana Anda menggunakan wadah.
Katakanlah
Client
kebutuhanDependency
. Wadah tersebut memiliki aDependency
.Kami baru saja mengikuti pola pencari lokasi layanan karena
Client
tahu cara menemukanDependency
. Tentu itu menggunakan kode kerasClassPathXmlApplicationContext
tetapi bahkan jika Anda menyuntikkan bahwa Anda masih memiliki pencari layanan karenaClient
panggilanbeanfactory.getBean()
.Untuk menghindari pencari layanan, Anda tidak harus meninggalkan wadah ini. Anda hanya perlu memindahkannya
Client
sehinggaClient
tidak tahu tentang itu.Perhatikan bagaimana
Client
sekarang tidak tahu wadah itu ada:Pindahkan wadah dari semua klien dan tempelkan di tempat utama di mana ia dapat membuat grafik objek dari semua objek yang berumur panjang. Pilih salah satu objek untuk mengekstrak dan memanggil metode di atasnya dan Anda mulai mencentang seluruh grafik.
Itu memindahkan semua konstruksi statis ke dalam wadah XML namun membuat semua klien Anda tidak tahu bagaimana menemukan dependensi mereka.
Tapi main masih tahu bagaimana menemukan dependensi! Ya itu. Tetapi dengan tidak menyebarkan pengetahuan itu di sekitar Anda telah menghindari masalah inti dari pencari layanan. Keputusan untuk menggunakan wadah sekarang dibuat di satu tempat dan dapat diubah tanpa menulis ulang ratusan klien.
sumber
Saya berpikir bahwa cara termudah untuk memahami perbedaan antara keduanya dan mengapa wadah DI jauh lebih baik daripada pelacak layanan adalah dengan memikirkan mengapa kita melakukan inversi ketergantungan pada awalnya.
Kami melakukan inversi dependensi sehingga setiap kelas secara eksplisit menyatakan dengan tepat apa yang menjadi tanggungannya untuk operasi. Kami melakukannya karena ini menciptakan kopling paling longgar yang bisa kita capai. Semakin longgar kopling, semakin mudah untuk menguji dan melakukan refactor (dan umumnya membutuhkan paling sedikit refactoring di masa depan karena kodenya lebih bersih).
Mari kita lihat kelas berikut:
Di kelas ini, kami secara eksplisit menyatakan bahwa kami memerlukan IOutputProvider dan tidak ada yang lain untuk membuat kelas ini berfungsi. Ini sepenuhnya dapat diuji dan memiliki ketergantungan pada satu antarmuka. Saya dapat memindahkan kelas ini ke mana saja dalam aplikasi saya, termasuk proyek yang berbeda dan yang diperlukan hanyalah akses ke antarmuka IOutputProvider. Jika pengembang lain ingin menambahkan sesuatu yang baru ke kelas ini, yang memerlukan ketergantungan kedua, mereka harus secara eksplisit tentang apa yang mereka butuhkan dalam konstruktor.
Lihatlah kelas yang sama dengan pencari lokasi:
Sekarang saya telah menambahkan locator layanan sebagai ketergantungan. Berikut adalah masalah yang langsung terlihat:
Jadi mengapa kita tidak menjadikan locator layanan sebagai kelas statis? Mari lihat:
Ini jauh lebih sederhana, bukan?
Salah.
Katakanlah IOutputProvider diimplementasikan oleh layanan web yang berjalan sangat lama yang menulis string dalam lima belas database berbeda di seluruh dunia dan membutuhkan waktu yang sangat lama untuk diselesaikan.
Mari kita coba kelas ini. Kami membutuhkan implementasi IOutputProvider yang berbeda untuk pengujian. Bagaimana cara kita menulis tes?
Nah untuk melakukan itu kita perlu melakukan beberapa konfigurasi mewah di kelas ServiceLocator statis untuk menggunakan implementasi IOutputProvider yang berbeda ketika sedang dipanggil oleh tes. Bahkan menulis kalimat itu menyakitkan. Menerapkannya akan menyiksa dan itu akan menjadi mimpi buruk pemeliharaan . Kita seharusnya tidak perlu memodifikasi kelas khusus untuk pengujian, terutama jika kelas itu bukan kelas yang kita coba untuk menguji.
Jadi sekarang Anda pergi dengan a) tes yang menyebabkan perubahan kode mencolok di kelas ServiceLocator yang tidak terkait; atau b) tidak ada tes sama sekali. Dan Anda pergi dengan solusi yang kurang fleksibel juga.
Jadi layanan kelas locator memiliki harus disuntikkan ke konstruktor. Yang berarti bahwa kita pergi dengan masalah khusus yang disebutkan sebelumnya. Pencari layanan memerlukan lebih banyak kode, memberi tahu pengembang lain bahwa ia membutuhkan hal-hal yang tidak diperlukan, mendorong pengembang lain menulis kode yang lebih buruk dan memberi kami lebih sedikit fleksibilitas untuk bergerak maju.
Sederhananya pelacak layanan meningkatkan sambungan dalam aplikasi dan mendorong pengembang lain untuk menulis kode yang sangat berpasangan .
sumber