Seperti biasa, Itu Tergantung ™. Jawabannya tergantung pada masalah yang coba dipecahkan. Dalam jawaban ini, saya akan mencoba mengatasi beberapa kekuatan motivasi yang umum:
Mendukung basis kode yang lebih kecil
Jika Anda memiliki 4.000 baris kode konfigurasi Spring, saya kira basis kode memiliki ribuan kelas.
Ini bukan masalah yang bisa Anda atasi setelah fakta, tetapi sebagai aturan praktis, saya cenderung lebih suka aplikasi yang lebih kecil, dengan basis kode yang lebih kecil. Jika Anda menyukai Domain-Driven Design , Anda dapat, misalnya, membuat basis kode per konteks terbatas.
Saya mendasarkan saran ini pada pengalaman saya yang terbatas, karena saya telah menulis kode lini bisnis berbasis web untuk sebagian besar karir saya. Saya bisa membayangkan bahwa jika Anda mengembangkan aplikasi desktop, atau sistem embedded, atau lainnya, hal-hal itu lebih sulit untuk dipisahkan.
Sementara saya menyadari bahwa saran pertama ini mudah dan paling tidak praktis, saya juga percaya itu adalah yang paling penting, dan itulah sebabnya saya memasukkannya. Kompleksitas kode bervariasi secara non-linear (mungkin secara eksponensial) dengan ukuran basis kode.
Favour Pure DI
Sementara saya masih menyadari bahwa pertanyaan ini menyajikan situasi yang ada, saya merekomendasikan Pure DI . Jangan gunakan Wadah DI, tetapi jika Anda melakukannya, setidaknya gunakan untuk menerapkan komposisi berbasis konvensi .
Saya tidak memiliki pengalaman praktis dengan Spring, tapi saya berasumsi bahwa dengan file konfigurasi , file XML tersirat.
Mengkonfigurasi dependensi menggunakan XML adalah yang terburuk dari kedua dunia. Pertama, Anda kehilangan keamanan tipe waktu kompilasi, tetapi Anda tidak mendapatkan apa pun. File konfigurasi XML dapat dengan mudah sebesar kode yang coba diganti.
Dibandingkan dengan masalah yang ingin ditangani, file konfigurasi injeksi dependensi menempati tempat yang salah pada jam kompleksitas konfigurasi .
Kasus untuk injeksi ketergantungan berbutir kasar
Saya bisa membuat kasus injeksi ketergantungan kasar. Saya juga dapat membuat case untuk injeksi dependensi berbutir halus (lihat bagian selanjutnya).
Jika Anda hanya menyuntikkan beberapa dependensi 'sentral', maka sebagian besar kelas mungkin terlihat seperti ini:
public class Foo
{
private readonly Bar bar;
public Foo()
{
this.bar = new Bar();
}
// Members go here...
}
Ini masih cocok Pola Desain 's mendukung komposisi objek atas warisan kelas , karena Foo
menyusun Bar
. Dari perspektif rawatan, ini masih dapat dianggap dapat dipertahankan, karena jika Anda perlu mengubah komposisi, Anda cukup mengedit kode sumber untuk Foo
.
Ini hampir tidak bisa dirawat daripada injeksi ketergantungan. Bahkan, saya akan mengatakan bahwa lebih mudah untuk langsung mengedit kelas yang menggunakan Bar
, daripada harus mengikuti tipuan yang melekat dengan injeksi ketergantungan.
Dalam edisi pertama buku saya tentang Ketergantungan Injeksi , saya membuat perbedaan antara dependensi stabil dan volatil.
Ketergantungan yang mudah menguap adalah ketergantungan yang harus Anda pertimbangkan untuk disuntikkan. Mereka termasuk
- Ketergantungan yang harus dikonfigurasi ulang setelah kompilasi
- Ketergantungan dikembangkan secara paralel oleh tim lain
- Ketergantungan dengan perilaku non-deterministik, atau perilaku dengan efek samping
Di lain pihak, dependensi yang stabil adalah dependensi yang berperilaku baik. Dalam arti tertentu, Anda dapat berargumen bahwa perbedaan ini membuat kasus untuk ketergantungan ketergantungan berbutir kasar, meskipun saya harus mengakui bahwa saya tidak sepenuhnya menyadari bahwa ketika saya menulis buku.
Namun, dari perspektif pengujian, ini membuat pengujian unit lebih sulit. Anda tidak dapat lagi menggunakan unit test Foo
independen Bar
. Seperti yang dijelaskan JB Rainsberger , tes integrasi mengalami ledakan kompleksitas kombinatorik. Anda benar-benar harus menulis puluhan ribu kasus uji jika Anda ingin mencakup semua jalur melalui integrasi bahkan 4-5 kelas.
Argumen lawannya adalah bahwa seringkali, tugas Anda bukanlah memprogram kelas. Tugas Anda adalah mengembangkan sistem yang memecahkan beberapa masalah khusus. Ini adalah motivasi di balik Pengembangan Perilaku-Didorong (BDD).
Pandangan lain tentang hal ini disampaikan oleh DHH, yang mengklaim bahwa TDD mengarah pada kerusakan desain yang disebabkan oleh tes . Dia juga menyukai pengujian integrasi berbutir kasar.
Jika Anda mengambil perspektif ini tentang pengembangan perangkat lunak, maka injeksi ketergantungan kasar menjadi masuk akal.
Kasus untuk injeksi ketergantungan berbutir halus
Injeksi ketergantungan berbutir halus, di sisi lain, dapat digambarkan sebagai menyuntikkan semua hal!
Perhatian utama saya tentang injeksi ketergantungan kasar adalah kritik yang diungkapkan oleh JB Rainsberger. Anda tidak dapat mencakup semua jalur kode dengan tes integrasi, karena Anda harus menulis ribuan, atau puluhan ribu, kasus uji untuk mencakup semua jalur kode.
Para pendukung BDD akan melawan dengan argumen bahwa Anda tidak perlu menutup semua jalur kode dengan tes. Anda hanya perlu menutup yang menghasilkan nilai bisnis.
Dalam pengalaman saya, bagaimanapun, semua jalur kode 'eksotis' juga akan mengeksekusi dalam penyebaran volume tinggi, dan jika tidak diuji, banyak dari mereka akan memiliki cacat dan menyebabkan pengecualian run-time (sering pengecualian referensi-nol).
Ini telah menyebabkan saya mendukung injeksi ketergantungan berbutir halus, karena memungkinkan saya untuk menguji invarian dari semua objek secara terpisah.
Mendukung pemrograman fungsional
Sementara saya condong ke arah injeksi ketergantungan berbutir halus, saya telah menggeser penekanan saya pada pemrograman fungsional, di antara alasan-alasan lain karena secara intrinsik dapat diuji .
Semakin Anda bergerak menuju kode SOLID, semakin fungsional jadinya . Cepat atau lambat, Anda sebaiknya mengambil risiko. Arsitektur fungsional adalah arsitektur Ports and Adapters , dan injeksi dependensi juga merupakan upaya Port dan Adapters . Perbedaannya, bagaimanapun, adalah bahwa bahasa seperti Haskell memberlakukan arsitektur itu melalui sistem tipenya.
Mendukung pemrograman fungsional yang diketik secara statis
Pada titik ini, saya pada dasarnya sudah menyerah pada pemrograman berorientasi objek (OOP), meskipun banyak masalah OOP secara intrinsik digabungkan ke bahasa mainstream seperti Java dan C # lebih dari konsep itu sendiri.
Masalah dengan bahasa OOP utama adalah bahwa hampir tidak mungkin untuk menghindari masalah ledakan kombinatorik, yang, jika tidak diuji, mengarah ke pengecualian run-time. Bahasa yang diketik secara statis seperti Haskell dan F #, di sisi lain, memungkinkan Anda untuk menyandikan banyak titik keputusan dalam sistem tipe. Ini berarti bahwa alih-alih harus menulis ribuan tes, kompiler hanya akan memberi tahu Anda apakah Anda telah berurusan dengan semua jalur kode yang mungkin (sampai batas tertentu; itu bukan peluru perak).
Juga, injeksi ketergantungan tidak berfungsi . Pemrograman fungsional sejati harus menolak seluruh gagasan dependensi . Hasilnya adalah kode yang lebih sederhana.
Ringkasan
Jika terpaksa bekerja dengan C #, saya lebih suka injeksi dependensi berbutir halus karena memungkinkan saya untuk menutupi seluruh basis kode dengan sejumlah kasus uji yang dapat dikelola.
Pada akhirnya, motivasi saya adalah umpan balik yang cepat. Namun, pengujian unit bukan satu-satunya cara untuk mendapatkan umpan balik .