Saya baru-baru ini bekerja pada proyek Python di mana kami melakukan injeksi ketergantungan berat (karena kami harus agar aplikasi dapat diuji), tetapi kami tidak menggunakan kerangka kerja apa pun. Kadang-kadang agak membosankan untuk memasang semua dependensi secara manual, tetapi secara keseluruhan itu bekerja dengan baik.
Ketika sebuah objek harus dibuat di banyak tempat, kami hanya memiliki fungsi (kadang-kadang metode kelas dari objek itu) untuk membuat instance produksi. Fungsi ini dipanggil setiap kali kami membutuhkan objek itu.
Dalam pengujian, kami melakukan hal yang sama: jika beberapa kali kami perlu membuat 'versi uji' dari suatu objek (yaitu, instance nyata yang didukung oleh objek tiruan), kami memiliki fungsi untuk membuat 'versi uji' ini kelas, untuk digunakan dalam tes.
Seperti yang saya katakan, umumnya ini bekerja dengan baik.
Saya sekarang memasuki proyek Java baru, dan kami memutuskan apakah akan menggunakan kerangka kerja DI, atau melakukan DI secara manual (seperti dalam proyek sebelumnya).
Tolong jelaskan bagaimana bekerja dengan kerangka kerja DI akan berbeda, dan dalam cara apa itu lebih baik atau lebih buruk daripada injeksi ketergantungan vanilla manual.
Sunting: Saya juga ingin menambahkan pertanyaan: apakah Anda menyaksikan proyek 'tingkat profesional' yang melakukan DI secara manual, tanpa kerangka kerja?
sumber
Jawaban:
Kunci dari wadah DI adalah abstraksi . Wadah abstrak abstrak tentang desain ini untuk Anda. Seperti yang Anda tebak, itu memiliki dampak yang nyata pada kode, sering diterjemahkan ke dalam sejumlah konstruktor, setter, pabrik dan pembangun yang lebih sedikit. Karena itu, mereka membuat kode Anda lebih longgar digabungkan (karena IOC yang mendasarinya ), lebih bersih dan lebih sederhana. Meski bukan tanpa biaya.
Latar belakang
Bertahun-tahun yang lalu, abstraksi seperti itu mengharuskan kami untuk menulis berbagai file konfigurasi ( pemrograman deklaratif ). Sungguh luar biasa melihat jumlah LOC berkurang secara substansial, tetapi sementara LOCS menurun, jumlah file konfigurasi sedikit meningkat. Untuk proyek-proyek menengah atau kecil itu bukan masalah sama sekali, tetapi untuk proyek-proyek yang lebih besar, untuk memiliki "perakitan kode" yang tersebar 50% antara kode dan deskriptor XML menjadi masalah di ... Meskipun demikian, masalah utama adalah Paradigma itu sendiri. Itu cukup tidak fleksibel karena deskriptor menyisakan sedikit ruang untuk penyesuaian.
Paradigma bergeser semakin ke konvensi konfigurasi , yang memperdagangkan file konfigurasi untuk anotasi atau atribut. Itu membuat kode kami lebih bersih dan sederhana sambil memberikan kami fleksibilitas yang XML tidak bisa.
Mungkin, ini adalah perbedaan paling signifikan terkait dengan bagaimana Anda bekerja sampai sekarang. Lebih sedikit kode dan lebih banyak sihir.
Di sisi bawah ...
Konvensi tentang konfigurasi membuat abstraksi begitu berat sehingga Anda hampir tidak tahu cara kerjanya. Kadang-kadang tampak ajaib dan inilah satu kelemahan lagi. Setelah berhasil, banyak pengembang tidak peduli tentang cara kerjanya.
Ini adalah hambatan bagi mereka yang belajar DI dengan wadah DI. Mereka tidak sepenuhnya memahami relevansi pola desain, praktik yang baik dan prinsip-prinsip yang disarikan oleh wadah. Saya telah bertanya kepada pengembang apakah mereka terbiasa dengan DI dan kelebihannya dan mereka menjawab: - Ya, saya telah menggunakan Spring IoC -. (Apa artinya itu?!?)
Seseorang dapat setuju atau tidak dengan masing-masing "kelemahan" ini. Menurut pendapat saya yang sederhana, adalah suatu keharusan untuk mengetahui apa yang terjadi dalam proyek Anda. Kalau tidak, kita tidak memiliki pemahaman penuh tentang itu. Juga untuk mengetahui untuk apa DI dan untuk memiliki gagasan tentang bagaimana mengimplementasikannya merupakan nilai tambah untuk dipertimbangkan.
Di sisi positifnya ...
Produktivitas satu kata . Kerangka kerja mari kita fokus pada apa yang benar-benar penting. Bisnis aplikasi.
Terlepas dari kekurangan yang dikomentari, bagi banyak dari kita (pekerjaan yang dikondisikan oleh tenggat waktu dan biaya), alat-alat ini merupakan sumber daya yang tak ternilai. Menurut pendapat saya, ini harus menjadi argumen utama kami dalam mendukung penerapan wadah DI. Produktivitas .
Pengujian
Apakah jika kita menerapkan DI murni atau wadah, pengujian tidak akan menjadi masalah. Justru sebaliknya. Wadah yang sama sering memberi kita ejekan untuk pengujian unit di luar kotak. Selama bertahun-tahun saya telah menggunakan tes saya sebagai termometer. Mereka memberi tahu saya jika saya sangat mengandalkan fasilitas wadah. Saya mengatakan ini karena kontainer ini dapat menginisialisasi atribut pribadi tanpa setter atau konstruktor. Mereka dapat menyuntikkan komponen hampir di sembarang tempat!
Kedengarannya menggoda bukan? Hati-hati! Jangan jatuh ke dalam perangkap !!
Saran
Jika Anda memutuskan untuk menerapkan wadah, saya sangat menyarankan tetap berpegang pada praktik yang baik. Terus menerapkan konstruktor, setter dan antarmuka. Menerapkan kerangka / wadah untuk menggunakannya . Ini akan mempermudah migrasi yang mungkin ke kerangka kerja lain atau menghapus yang sebenarnya. Ini dapat mengurangi secara signifikan ketergantungan pada wadah.
Mengenai praktik ini:
Kapan menggunakan wadah DI
Jawaban saya akan menjadi bias oleh pengalaman sendiri yang terutama mendukung wadah DI (seperti yang saya komentari, saya sangat fokus pada produktivitas, jadi saya tidak pernah memiliki kebutuhan DI murni. Justru sebaliknya).
Saya pikir Anda akan menemukan artikel Mark Seeman yang menarik tentang subjek ini, yang juga menjawab pertanyaan tentang bagaimana menerapkan DI murni .
Akhirnya, jika kita berbicara tentang Jawa, saya akan mempertimbangkan terlebih dahulu melihat ke JSR330 - Dependency Injection .
Meringkas
Manfaat :
Kekurangan :
Perbedaan dengan DI murni :
sumber
Dalam pengalaman saya, kerangka injeksi ketergantungan menyebabkan sejumlah masalah. Beberapa masalah akan tergantung pada kerangka kerja dan perangkat. Mungkin ada praktik yang mengurangi masalah. Tapi ini adalah masalah yang saya tekan.
Objek non-global
Dalam beberapa kasus, Anda ingin menyuntikkan objek global: objek yang hanya memiliki satu instance di aplikasi yang sedang berjalan. Ini mungkin a
MarkdownToHtmlRendererer
, aDatabaseConnectionPool
, alamat untuk server api Anda, dll. Untuk kasus ini, kerangka kerja injeksi ketergantungan bekerja sangat sederhana, Anda cukup menambahkan ketergantungan yang diperlukan ke konstruktor Anda dan Anda sudah mendapatkannya.Tetapi bagaimana dengan benda-benda yang tidak bersifat global? Bagaimana jika Anda membutuhkan pengguna untuk permintaan saat ini? Bagaimana jika Anda memerlukan transaksi basis data yang aktif saat ini? Bagaimana jika Anda memerlukan elemen HTML yang sesuai dengan div yang berisi formulir yang merupakan kotak teks yang baru saja memecat suatu acara?
Solusi yang saya lihat dipekerjakan oleh kerangka kerja DI adalah injector hirarkis. Tetapi ini membuat model injeksi lebih rumit. Menjadi lebih sulit untuk mencari tahu apa yang akan disuntikkan dari lapisan hirarki apa. Menjadi sulit untuk mengatakan apakah objek yang diberikan akan ada per-aplikasi, per-pengguna, per-permintaan, dll. Anda tidak bisa lagi hanya menambahkan dependensi Anda dan membuatnya berfungsi.
Perpustakaan
Seringkali Anda ingin memiliki perpustakaan kode yang dapat digunakan kembali. Ini juga akan mencakup instruksi bagaimana membangun objek dari kode itu. Jika Anda menggunakan kerangka kerja injeksi ketergantungan, ini biasanya akan mencakup aturan yang mengikat. Jika Anda ingin menggunakan perpustakaan, Anda menambahkan aturan yang mengikat ke Anda.
Namun, berbagai masalah bisa terjadi. Anda mungkin berakhir dengan kelas yang sama terikat pada implementasi yang berbeda. Perpustakaan mungkin berharap ada ikatan tertentu. Perpustakaan dapat mengganti beberapa binding standar yang menyebabkan kode Anda melakukan hal yang aneh. Kode Anda mungkin menimpa beberapa ikatan default yang menyebabkan kode perpustakaan melakukan hal-hal aneh.
Kompleksitas Tersembunyi
Ketika Anda menulis pabrik secara manual, Anda memiliki perasaan tentang bagaimana dependensi aplikasi cocok bersama. Saat Anda menggunakan kerangka kerja injeksi ketergantungan, Anda tidak melihat ini. Sebaliknya, objek cenderung tumbuh semakin banyak ketergantungan hingga cepat atau lambat semuanya tergantung pada hampir semua.
Dukungan IDE
IDE Anda mungkin tidak akan memahami kerangka kerja injeksi ketergantungan. Dengan pabrik manual, Anda dapat menemukan semua penelepon konstruktor untuk mengetahui semua tempat yang mungkin dibangun oleh objek. Tapi itu tidak akan menemukan panggilan yang dihasilkan oleh kerangka kerja DI.
Kesimpulan
Apa yang kita dapatkan dari kerangka injeksi ketergantungan? Kita harus menghindari beberapa boilerplate berpikiran sederhana yang diperlukan untuk membuat instance objek. Saya semua untuk menghilangkan boilerplate berpikiran sederhana. Namun, mempertahankan boilerplate yang berpikiran sederhana itu mudah. Jika kita akan mengganti pelat itu, kita harus bersikeras untuk menggantinya dengan sesuatu yang lebih mudah. Karena berbagai masalah yang telah saya bahas di atas, saya tidak berpikir kerangka kerja (setidaknya karena mereka berdiri) sesuai dengan tagihan.
sumber
Apakah Java + dependency injection = framework diperlukan?
Tidak.
Apa yang saya beli dari kerangka kerja DI?
Konstruksi objek terjadi dalam bahasa yang bukan Java.
Jika metode pabrik cukup baik untuk kebutuhan Anda, Anda dapat melakukan DI di java sepenuhnya framework gratis di 100% java asli. Jika kebutuhan Anda melampaui itu Anda memiliki pilihan lain.
Pola desain Geng Empat mencapai banyak hal besar tetapi selalu memiliki kelemahan dalam hal pola penciptaan. Java sendiri memiliki beberapa kelemahan di sini. Kerangka kerja DI benar-benar membantu dengan kelemahan kreasi ini lebih dari yang mereka lakukan dengan suntikan yang sebenarnya. Anda sudah tahu bagaimana melakukannya tanpa mereka. Seberapa baik mereka akan melakukan Anda tergantung pada berapa banyak bantuan yang Anda butuhkan dengan konstruksi objek.
Ada banyak pola penciptaan yang muncul setelah Geng Empat. Pembangun Josh Bloch adalah peretasan hebat seputar kekurangan parameter bernama java. Di luar itu adalah pembangun iDSL yang menawarkan banyak fleksibilitas. Tetapi masing-masing mewakili pekerjaan yang signifikan dan boilerplate.
Kerangka kerja dapat menyelamatkan Anda dari beberapa kebosanan ini. Mereka bukannya tanpa kekurangan. Jika Anda tidak berhati-hati, Anda dapat menemukan diri Anda bergantung pada kerangka kerja. Coba beralih kerangka kerja setelah menggunakan satu dan lihat sendiri. Bahkan jika Anda entah bagaimana mempertahankan xml atau anotasi secara umum, Anda dapat akhirnya mendukung apa yang pada dasarnya adalah dua bahasa yang harus Anda sebutkan berdampingan ketika Anda mengiklankan pekerjaan pemrograman.
Sebelum Anda terlalu terpikat dengan kekuatan kerangka kerja DI, luangkan waktu dan selidiki kerangka kerja lain yang memberi Anda kemampuan yang mungkin Anda anggap membutuhkan kerangka kerja DI untuk Anda. Banyak yang bercabang di luar DI sederhana . Pertimbangkan: Lombok , AspectJ , dan slf4J . Masing-masing tumpang tindih dengan beberapa kerangka kerja DI tetapi masing-masing bekerja dengan baik sendiri.
Jika pengujian benar-benar merupakan kekuatan pendorong di balik ini, silakan lihat: jUnit (semua xUnit benar-benar) dan Mockito (mock apa saja) sebelum Anda mulai berpikir bahwa kerangka kerja DI harus mengambil alih hidup Anda.
Anda dapat melakukan apa yang Anda suka, tetapi untuk pekerjaan serius, saya lebih suka kotak alat yang berpenduduk baik daripada pisau tentara Swiss. Berharap lebih mudah untuk menggambar garis-garis ini tetapi beberapa telah bekerja keras untuk mengaburkannya. Tidak ada yang salah dengan itu tetapi bisa membuat pilihan ini menantang.
sumber
Membuat kelas dan menugaskan mereka dependensi adalah bagian dari proses pemodelan, bagian yang menyenangkan. Itulah alasan mengapa sebagian besar programmer menikmati pemrograman.
Memutuskan, implementasi mana yang akan digunakan dan bagaimana kelas dibangun adalah hal yang harus diimplementasikan entah bagaimana dan merupakan bagian dari konfigurasi aplikasi.
Menulis pabrik secara manual adalah pekerjaan yang membosankan. Kerangka kerja DI mempermudahnya dengan secara otomatis menyelesaikan dependensi yang mungkin diselesaikan dan membutuhkan konfigurasi untuk yang tidak.
Menggunakan IDE modern memungkinkan Anda menavigasi metode dan penggunaannya. Memiliki pabrik yang ditulis secara manual cukup baik, karena Anda dapat menyorot konstruktor kelas, menunjukkan penggunaannya, dan itu akan menunjukkan kepada Anda semua tempat di mana dan bagaimana kelas tertentu sedang dibangun.
Tetapi bahkan dengan kerangka kerja DI, Anda dapat memiliki tempat sentral, seperti konfigurasi konstruksi grafik objek (OGCC mulai sekarang), dan jika sebuah kelas bergantung pada dependensi ambigu, Anda dapat dengan mudah melihat ke dalam OGCC dan melihat bagaimana kelas tertentu sedang dibangun disana.
Anda bisa keberatan, yang menurut Anda secara pribadi dapat melihat penggunaan konstruktor tertentu adalah nilai tambah yang bagus. Saya akan mengatakan apa yang lebih baik bagi Anda tidak hanya subjektif, tetapi sebagian besar berasal dari pengalaman pribadi.
Jika Anda selalu bekerja pada proyek yang mengandalkan kerangka kerja DI, Anda akan benar-benar tahu hanya itu dan ketika Anda ingin melihat konfigurasi kelas, Anda akan selalu melihat ke OGCC dan bahkan tidak akan mencoba untuk melihat bagaimana konstruktor digunakan , karena Anda akan tahu semua dependensi terhubung secara otomatis pula.
Tentu saja, kerangka kerja DI tidak dapat digunakan untuk semuanya dan setiap sekarang dan kemudian Anda harus menulis satu atau dua metode pabrik. Kasus yang sangat umum adalah membangun ketergantungan selama iterasi di atas koleksi, di mana setiap iterasi memasok bendera yang berbeda yang harus menghasilkan implementasi yang berbeda dari antarmuka yang umum.
Pada akhirnya, kemungkinan besar semuanya akan mengarah pada apa preferensi Anda dan apa yang ingin Anda korbankan (baik saat menulis pabrik atau transparansi).
Pengalaman pribadi (peringatan: subyektif)
Berbicara sebagai pengembang PHP, meskipun saya menyukai ide di balik kerangka kerja DI, saya hanya pernah menggunakannya sekali. Saat itulah tim saya bekerja dengan seharusnya menyebarkan aplikasi sangat cepat dan kami tidak bisa kehilangan waktu menulis pabrik. Jadi kami hanya mengambil kerangka DI, mengkonfigurasinya, dan itu berhasil, entah bagaimana .
Untuk sebagian besar proyek saya ingin pabrik-pabrik ditulis secara manual karena saya tidak suka ilmu hitam terjadi dalam kerangka kerja DI. Saya adalah orang yang bertanggung jawab untuk memodelkan sebuah kelas dan dependensinya. Saya adalah orang yang memutuskan mengapa desain terlihat seperti itu. Jadi saya akan menjadi orang yang benar membangun kelas (bahkan jika kelas membutuhkan dependensi keras, seperti kelas beton bukan antarmuka).
sumber
Secara pribadi saya tidak menggunakan wadah DI dan tidak menyukainya. Saya punya beberapa alasan lagi untuk itu.
Biasanya ini adalah ketergantungan statis. Jadi itu hanya pencari lokasi layanan dengan semua kekurangannya. Kemudian, karena sifat dependensi statis, mereka disembunyikan. Anda tidak perlu berurusan dengan mereka, mereka entah bagaimana sudah ada di sana, dan itu berbahaya, karena Anda berhenti mengendalikan mereka.
Ini melanggar prinsip-prinsip OOP , seperti kohesi dan metafora objek Lego-bata David West .
Jadi membangun seluruh objek sedekat mungkin dengan titik masuk program adalah cara untuk pergi.
sumber
Seperti yang sudah Anda perhatikan: Bergantung pada bahasa yang Anda gunakan, ada beberapa sudut pandang yang berbeda, berbeda cara menangani masalah yang sama.
Mengenai injeksi dependensi, turun ke ini: injeksi dependensi, seperti istilahnya, tidak lebih dari meletakkan dependensi yang dibutuhkan satu objek. ke objek itu - kontras dengan cara lain: membiarkan objek itu sendiri membuat instance instance objek yang bergantung padanya. Anda memisahkan kreasi objek dan konsumsi objek untuk mendapatkan lebih banyak fleksibilitas.
Jika desain Anda ditata dengan baik, Anda biasanya memiliki beberapa kelas dengan banyak dependensi, oleh karena itu pemasangan dependensi - baik dengan tangan atau kerangka kerja - harus relatif mudah dan jumlah pelat untuk menulis tes harus mudah dikelola.
Yang mengatakan, tidak perlu untuk DI-framework.
Namun mengapa Anda ingin menggunakan kerangka kerja?
I) Hindari kode boilerplate
Jika proyek Anda mendapatkan ukuran, di mana Anda merasakan sakitnya menulis pabrik (dan instatiasi) berulang kali, Anda harus menggunakan kerangka kerja DI
II) Pendekatan declacrative untuk pemrograman
Ketika Java pernah pemrograman dengan XML sekarang: menaburkan fairydust dengan anotasi dan keajaiban terjadi .
Tapi serius: Saya melihat sisi positifnya . Anda dengan jelas mengomunikasikan niat dengan mengatakan apa yang dibutuhkan alih-alih di mana menemukannya dan bagaimana membangunnya.
tl; dr
Kerangka kerja DI tidak membuat basis kode Anda lebih baik secara ajaib, tetapi membantu membuat kode yang tampak cantik terlihat sangat jernih . Gunakan itu, ketika Anda merasa Anda membutuhkannya. Pada titik tertentu dalam proyek Anda, itu akan menghemat waktu Anda dan mencegah mual.
sumber
Iya nih. Anda mungkin harus membaca buku Mark Seemann berjudul "Dependency Injection". Dia menguraikan beberapa praktik yang baik. Berdasarkan beberapa dari apa yang Anda katakan, Anda mungkin mendapat manfaat. Anda harus bertujuan untuk memiliki satu root komposisi atau satu root komposisi per unit pekerjaan (misalnya, permintaan web). Saya suka cara berpikirnya bahwa objek apa pun yang Anda buat dianggap sebagai dependensi (seperti halnya parameter apa pun untuk metode / konstruktor) sehingga Anda harus benar-benar berhati-hati di mana dan bagaimana Anda membuat objek. Kerangka kerja akan membantu Anda menjaga konsep ini tetap jelas. Jika Anda membaca buku Markus, Anda akan menemukan lebih dari sekadar jawaban atas pertanyaan Anda.
sumber
Ya, ketika bekerja di Jawa, saya akan merekomendasikan kerangka kerja DI. Ada beberapa alasan untuk ini:
Java memiliki beberapa paket DI yang sangat baik yang menawarkan injeksi dependensi otomatis dan juga biasanya dikemas dengan kemampuan tambahan yang lebih sulit untuk ditulis secara manual daripada DI sederhana (mis. Dependensi cakupan yang memungkinkan koneksi otomatis antar cakupan dengan membuat kode untuk mencari lingkup apa yang seharusnya di gunakan d untuk menemukan versi dependensi yang benar untuk lingkup itu - jadi misalnya objek aplikasi lingkup dapat merujuk ke permintaan yang mencakup wilayah).
anotasi java sangat berguna dan menyediakan cara yang sangat baik untuk mengkonfigurasi dependensi
konstruksi objek java terlalu verbose dibandingkan dengan apa yang Anda terbiasa dengan python; menggunakan kerangka kerja di sini menghemat lebih banyak pekerjaan daripada dalam bahasa yang dinamis.
Java DI frameworks dapat melakukan hal-hal yang berguna seperti secara otomatis menghasilkan proxy dan dekorator yang lebih berfungsi di Jawa daripada bahasa yang dinamis.
sumber
Jika proyek Anda cukup kecil dan Anda tidak memiliki banyak pengalaman dengan DI-Framework saya akan merekomendasikan untuk tidak menggunakan kerangka kerja dan lakukan di secara manual.
Alasan: Saya pernah bekerja di proyek yang menggunakan dua perpustakaan yang memiliki ketergantungan langsung ke kerangka kerja yang berbeda. mereka berdua memiliki kerangka spesifik kode seperti di-give-me-an-instance-of-XYZ. Sejauh yang saya tahu Anda hanya dapat memiliki satu implementasi di-framework aktif pada suatu waktu.
dengan kode seperti ini Anda dapat melakukan lebih banyak kerugian daripada manfaat.
Jika Anda memiliki lebih banyak pengalaman Anda mungkin akan abstrak di-framwork oleh antarmuka (pabrik atau servicelocator) sehingga masalah ini tidak akan ada lagi dan kerangka kerja di akan melakukan lebih banyak manfaat yang membahayakan.
sumber