Saya mencoba memperkenalkan DI sebagai pola di sini di tempat kerja dan salah satu pengembang utama kami ingin tahu: Apa - jika ada - yang menjadi kelemahan menggunakan pola Injeksi Ketergantungan?
Catatan Saya mencari di sini untuk - jika mungkin - daftar lengkap, bukan diskusi subjektif tentang topik ini.
Klarifikasi : Saya berbicara tentang pola Injeksi Ketergantungan (lihat artikel ini oleh Martin Fowler), bukan kerangka kerja tertentu, apakah berbasis XML (seperti Spring) atau berbasis kode (seperti Guice), atau "linting" .
Sunting : Beberapa diskusi / ranting / debat hebat selanjutnya berlangsung / r / pemrograman di sini.
Jawaban:
Beberapa poin:
Secara umum, manfaat dari decoupling membuat setiap tugas lebih mudah untuk dibaca dan dipahami, tetapi meningkatkan kompleksitas mengatur tugas yang lebih kompleks.
sumber
Masalah dasar yang sama sering Anda dapatkan dengan pemrograman berorientasi objek, aturan gaya dan hampir semua hal lainnya. Itu mungkin - sangat umum, pada kenyataannya - untuk melakukan terlalu banyak abstraksi, dan menambahkan terlalu banyak tipuan, dan secara umum menerapkan teknik yang baik secara berlebihan dan di tempat yang salah.
Setiap pola atau konstruksi lain yang Anda terapkan membawa kompleksitas. Abstraksi dan tipuan menyebarkan informasi, kadang-kadang mengeluarkan detail yang tidak relevan, tetapi terkadang membuat lebih sulit untuk memahami dengan tepat apa yang terjadi. Setiap aturan yang Anda terapkan membawa ketidakfleksibelan, mengesampingkan opsi yang mungkin merupakan pendekatan terbaik.
Intinya adalah untuk menulis kode yang melakukan pekerjaan dan kuat, dapat dibaca, dan dipelihara. Anda adalah pengembang perangkat lunak - bukan pembuat menara gading.
Tautan yang Relevan
http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx
http://www.joelonsoftware.com/articles/fog0000000018.html
Mungkin bentuk injeksi ketergantungan yang paling sederhana (jangan tertawa) adalah sebuah parameter. Kode dependen tergantung pada data, dan data tersebut diinjeksi dengan cara melewatkan parameter.
Ya, itu konyol dan tidak membahas titik berorientasi objek injeksi ketergantungan, tetapi seorang programmer fungsional akan memberi tahu Anda bahwa (jika Anda memiliki fungsi kelas satu) ini adalah satu-satunya jenis injeksi ketergantungan yang Anda butuhkan. Intinya di sini adalah untuk mengambil contoh sepele, dan menunjukkan potensi masalah.
Mari kita ambil fungsi tradisional yang sederhana ini - sintaks C ++ tidak signifikan di sini, tapi saya harus mengejanya ...
Saya memiliki ketergantungan, saya ingin mengekstrak dan menyuntikkan - teks "Hello World". Cukup mudah...
Bagaimana itu lebih tidak fleksibel daripada aslinya? Nah, bagaimana jika saya memutuskan bahwa outputnya harus unicode. Saya mungkin ingin beralih dari std :: cout ke std :: wcout. Tapi itu berarti string saya harus dari wchar_t, bukan dari char. Baik setiap pemanggil harus diubah, atau (lebih masuk akal), implementasi lama akan diganti dengan adaptor yang menerjemahkan string dan memanggil implementasi baru.
Itu pekerjaan pemeliharaan di sana yang tidak diperlukan jika kita menyimpan yang asli.
Dan jika tampaknya sepele, lihatlah fungsi dunia nyata ini dari API Win32 ...
http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx
Itu 12 "dependensi" yang harus dihadapi. Misalnya, jika resolusi layar menjadi sangat besar, mungkin kita perlu nilai koordinat 64-bit - dan versi lain dari CreateWindowEx. Dan ya, sudah ada versi lama yang masih berkeliaran, yang mungkin dipetakan ke versi yang lebih baru di belakang layar ...
http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx
"Ketergantungan" itu bukan hanya masalah bagi pengembang asli - setiap orang yang menggunakan antarmuka itu harus mencari tahu apa dependensinya, bagaimana mereka ditentukan, dan apa artinya, dan mencari tahu apa yang harus dilakukan untuk aplikasi mereka. Di sinilah kata "default yang masuk akal" dapat membuat hidup lebih sederhana.
Injeksi ketergantungan objek-berorientasi pada prinsipnya tidak berbeda. Menulis kelas adalah overhead, baik dalam teks kode sumber dan waktu pengembang, dan jika kelas itu ditulis untuk memasok dependensi sesuai dengan beberapa spesifikasi objek dependen, maka objek dependen dikunci ke dalam mendukung antarmuka itu, bahkan jika ada kebutuhan untuk mengganti implementasi objek itu.
Tak satu pun dari ini harus dibaca sebagai mengklaim bahwa injeksi ketergantungan buruk - jauh dari itu. Tetapi teknik yang baik dapat diterapkan secara berlebihan dan di tempat yang salah. Sama seperti tidak setiap string perlu diekstraksi dan diubah menjadi parameter, tidak setiap perilaku tingkat rendah perlu diekstraksi dari objek tingkat tinggi dan berubah menjadi ketergantungan injeksi.
sumber
Inilah reaksi awal saya sendiri: Pada dasarnya kerugian yang sama dari pola apa pun.
sumber
"Kelemahan" terbesar untuk Inversion of Control (tidak cukup DI, tetapi cukup dekat) adalah bahwa ia cenderung menghilangkan memiliki satu titik untuk melihat gambaran umum suatu algoritma. Itu pada dasarnya apa yang terjadi ketika Anda memiliki kode terpisah, kemampuan untuk melihat di satu tempat adalah artefak kopling ketat.
sumber
Saya kira daftar itu tidak ada, tetapi cobalah membaca artikel-artikel itu:
DI dapat mengaburkan kode (jika Anda tidak bekerja dengan IDE yang baik)
Menyalahgunakan IoC dapat menyebabkan kode buruk menurut Paman Bob.
Perlu diwaspadai untuk over-engineering dan menciptakan fleksibilitas yang tidak perlu.
sumber
Saya telah menggunakan Guice (kerangka Java DI) secara luas selama 6 bulan terakhir. Sementara secara keseluruhan saya pikir itu hebat (terutama dari perspektif pengujian), ada kelemahan tertentu. Terutama:
Sekarang saya sudah mengeluh. Biarkan saya mengatakan bahwa saya akan terus (dengan sukarela) menggunakan Guice dalam proyek saya saat ini dan kemungkinan besar saya berikutnya. Injeksi ketergantungan adalah pola yang hebat dan sangat kuat. Tapi itu pasti bisa membingungkan dan Anda hampir pasti akan menghabiskan waktu mengutuk pada kerangka injeksi ketergantungan apa pun yang Anda pilih.
Juga, saya setuju dengan poster lain bahwa ketergantungan injeksi dapat digunakan secara berlebihan.
sumber
Kode tanpa DI menjalankan risiko yang terkenal dari terjerat ke dalam kode Spaghetti - beberapa gejala adalah bahwa kelas dan metode terlalu besar, melakukan terlalu banyak dan tidak dapat dengan mudah diubah, dipecah, di refactored, atau diuji.
Kode dengan DI banyak digunakan dapat kode Ravioli di mana setiap kelas kecil seperti nugget ravioli individu - itu melakukan satu hal kecil dan prinsip tanggung jawab tunggal dipatuhi, yang baik. Tetapi melihat kelas sendiri, sulit untuk melihat apa yang dilakukan sistem secara keseluruhan, karena ini tergantung pada bagaimana semua bagian kecil ini bersatu, yang sulit dilihat. Itu hanya tampak seperti tumpukan besar hal-hal kecil.
Dengan menghindari kompleksitas spageti bit besar kode digabungkan dalam kelas besar, Anda berisiko risiko jenis kompleksitas lain, di mana ada banyak kelas kecil yang sederhana dan interaksi di antara mereka sangat kompleks.
Saya tidak berpikir bahwa ini adalah kerugian fatal - DI masih sangat bermanfaat. Beberapa derajat gaya ravioli dengan kelas-kelas kecil yang melakukan hanya satu hal mungkin bagus. Bahkan secara berlebihan, saya tidak berpikir itu buruk seperti kode spageti. Tetapi menyadari bahwa itu bisa dilakukan terlalu jauh adalah langkah pertama untuk menghindarinya. Ikuti tautan untuk diskusi tentang cara menghindarinya.
sumber
Jika Anda memiliki solusi buatan sendiri, dependensi tepat berada di wajah Anda di konstruktor. Atau mungkin sebagai parameter metode yang lagi-lagi tidak terlalu sulit dikenali. Meskipun dependensi kerangka kerja dikelola, jika dibawa ke ekstrem, dapat mulai tampak seperti sihir.
Namun, memiliki terlalu banyak dependensi di terlalu banyak kelas adalah tanda yang jelas bahwa struktur kelas Anda kacau. Jadi dengan cara injeksi ketergantungan (buatan sendiri atau kerangka yang dikelola) dapat membantu mengeluarkan masalah desain mencolok yang mungkin tersembunyi bersembunyi di kegelapan.
Untuk mengilustrasikan poin kedua dengan lebih baik, berikut adalah kutipan dari artikel ini ( sumber asli ) yang saya yakin sepenuh hati adalah masalah mendasar dalam membangun sistem apa pun, bukan hanya sistem komputer.
Apakah DI mengatasi masalah ini? Tidak ada . Tapi itu membantu Anda melihat dengan jelas jika Anda mencoba mendelegasikan tanggung jawab merancang setiap kamar kepada penghuninya.
sumber
Satu hal yang membuat saya sedikit menggeliat dengan DI adalah asumsi bahwa semua objek yang disuntikkan murah untuk instantiate dan tidak menghasilkan efek samping - ATAU - ketergantungan sering digunakan sehingga melebihi biaya instantiasi terkait.
Di mana ini bisa menjadi signifikan adalah ketika ketergantungan tidak sering digunakan dalam kelas konsumsi; seperti sesuatu seperti
IExceptionLogHandlerService
. Jelas, layanan seperti ini dipanggil (semoga :)) jarang di dalam kelas - mungkin hanya pada pengecualian yang perlu dicatat; namun pola konstruktor-injeksi kanonik ...... mensyaratkan bahwa contoh "langsung" dari layanan ini disediakan, mengutuk biaya / efek samping yang diperlukan untuk mendapatkannya di sana. Bukan berarti itu akan terjadi, tetapi bagaimana jika membangun instance dependensi ini melibatkan hit layanan / database, atau mencari file konfigurasi, atau mengunci sumber daya hingga dibuang? Jika layanan ini dibangun sesuai kebutuhan, berlokasi layanan, atau buatan pabrik (semua memiliki masalah sendiri), maka Anda akan mengambil biaya konstruksi hanya bila diperlukan.
Sekarang, ini adalah prinsip perancangan perangkat lunak yang diterima secara umum bahwa membangun objek itu murah dan tidak menghasilkan efek samping. Dan sementara itu gagasan yang bagus, itu tidak selalu terjadi. Namun, menggunakan injeksi konstruktor tipikal pada dasarnya menuntut hal ini. Berarti ketika Anda membuat implementasi ketergantungan, Anda harus mendesainnya dengan mempertimbangkan DI. Mungkin Anda akan membuat konstruksi objek lebih mahal untuk mendapatkan manfaat di tempat lain, tetapi jika implementasi ini akan disuntikkan, kemungkinan akan memaksa Anda untuk mempertimbangkan kembali desain itu.
Ngomong-ngomong, teknik-teknik tertentu dapat mengurangi masalah ini dengan memungkinkan pemuatan malas dari dependensi yang disuntikkan, misalnya memberikan kelas
Lazy<IService>
contoh sebagai dependensi. Ini akan mengubah konstruktor objek dependen Anda dan menjadikannya lebih menyadari detail implementasi seperti biaya konstruksi objek, yang mungkin juga tidak diinginkan.sumber
this.errorLogger.WriteError(ex)
ketika kesalahan terjadi dalam pernyataan try / catch.Ilusi bahwa Anda telah memisahkan kode Anda hanya dengan menerapkan injeksi dependensi tanpa benar-benar memisahkannya. Saya pikir itu hal yang paling berbahaya tentang DI.
sumber
Ini lebih dari sebuah nitpick. Tetapi salah satu kelemahan dari injeksi ketergantungan adalah membuatnya sedikit lebih sulit bagi alat pengembangan untuk memikirkan dan menavigasi kode.
Khususnya, jika Anda Kontrol-Klik / Perintah-Klik pada permohonan metode dalam kode, itu akan membawa Anda ke deklarasi metode pada antarmuka, bukan implementasi konkret.
Ini benar-benar lebih merupakan kelemahan dari kode yang digabungkan secara longgar (kode yang dirancang oleh antarmuka), dan berlaku bahkan jika Anda tidak menggunakan injeksi dependensi (yaitu, bahkan jika Anda hanya menggunakan pabrik). Tetapi kedatangan injeksi ketergantungan adalah apa yang benar-benar mendorong kode digabungkan secara longgar kepada massa, jadi saya pikir saya akan menyebutkannya.
Juga, manfaat dari kode yang digabungkan secara longgar jauh lebih besar daripada ini, jadi saya menyebutnya nitpick. Meskipun saya telah bekerja cukup lama untuk mengetahui bahwa ini adalah semacam push-back yang mungkin Anda dapatkan jika Anda mencoba untuk memperkenalkan injeksi ketergantungan.
Bahkan, saya berani menebak bahwa untuk setiap "downside" yang dapat Anda temukan untuk injeksi ketergantungan, Anda akan menemukan banyak sisi baiknya yang jauh melebihi itu.
sumber
Injeksi ketergantungan berbasis konstruktor (tanpa bantuan "kerangka" ajaib) adalah cara yang bersih dan bermanfaat untuk menyusun kode OO. Dalam basis kode terbaik yang pernah saya lihat, selama bertahun-tahun dihabiskan dengan mantan rekan kerja Martin Fowler, saya mulai memperhatikan bahwa sebagian besar kelas yang baik ditulis dengan cara ini akhirnya memiliki satu
doSomething
metode.Kelemahan utama, kemudian, adalah bahwa setelah Anda menyadari itu semua hanya cara OO lama menulis penutupan sebagai kelas untuk mendapatkan manfaat pemrograman fungsional, motivasi Anda untuk menulis kode OO dapat dengan cepat menguap.
sumber
Saya menemukan bahwa injeksi konstruktor dapat menyebabkan konstruktor jelek besar, (dan saya menggunakannya di seluruh basis kode saya - mungkin objek saya terlalu granular?). Juga, kadang-kadang dengan injeksi konstruktor saya berakhir dengan dependensi melingkar yang mengerikan (meskipun ini sangat jarang), sehingga Anda mungkin harus memiliki semacam siklus keadaan siap pakai dengan beberapa putaran injeksi ketergantungan dalam sistem yang lebih kompleks.
Namun, saya lebih menyukai injeksi konstrutor daripada injeksi setter karena begitu objek saya dikonstruksi, maka saya tahu tanpa ragu apa statusnya, apakah itu dalam lingkungan pengujian unit atau dimuat dalam beberapa wadah IOC. Yang, secara tidak langsung, mengatakan apa yang saya rasakan adalah kelemahan utama dengan injeksi setter.
(sebagai sidenote, saya menemukan seluruh topik cukup "religius", tetapi jarak tempuh Anda akan bervariasi dengan tingkat kefanatikan teknis dalam tim dev Anda!)
sumber
Jika Anda menggunakan DI tanpa wadah IOC, downside terbesar adalah Anda dengan cepat melihat berapa banyak dependensi kode Anda sebenarnya dan seberapa erat semuanya. ("Tapi saya pikir itu desain yang bagus!") Kemajuan alami adalah bergerak menuju wadah IOC yang dapat mengambil sedikit waktu untuk belajar dan mengimplementasikan (tidak seburuk kurva pembelajaran WPF, tapi itu tidak gratis antara). Kelemahan terakhir adalah beberapa pengembang akan mulai menulis jujur untuk tes unit kebaikan dan akan memakan waktu bagi mereka untuk mengetahuinya. Devs yang sebelumnya bisa melakukan sesuatu dalam setengah hari tiba-tiba akan menghabiskan dua hari mencoba mencari cara untuk mengejek semua ketergantungan mereka.
Mirip dengan jawaban Mark Seemann, intinya adalah bahwa Anda menghabiskan waktu menjadi pengembang yang lebih baik daripada meretas bit kode bersama-sama dan melemparkannya keluar pintu / ke dalam produksi. Bisnis apa yang ingin Anda miliki? Hanya Anda yang bisa menjawabnya.
sumber
DI adalah teknik atau pola dan tidak terkait dengan kerangka kerja apa pun. Anda dapat memasang dependensi Anda secara manual. DI membantu Anda dengan SR (Tanggung jawab tunggal) dan SoC (pemisahan masalah). DI mengarah ke desain yang lebih baik. Dari sudut pandang dan pengalaman saya, tidak ada kerugian . Seperti halnya pola lain, Anda bisa salah atau menyalahgunakannya (tetapi apa yang ada dalam kasus DI cukup sulit).
Jika Anda memperkenalkan DI sebagai prinsip untuk aplikasi lawas, menggunakan kerangka kerja - satu-satunya kesalahan terbesar yang dapat Anda lakukan adalah menyalahgunakannya sebagai Service-Locater. DI + Framework itu sendiri sangat bagus dan membuat segalanya menjadi lebih baik di mana saja saya melihatnya! Dari sudut pandang organisasi, ada masalah umum dengan setiap proses, teknik, pola baru, ...:
Secara umum Anda harus menginvestasikan waktu dan uang , di samping itu, tidak ada kerugian, sungguh!
sumber
Keterbacaan kode. Anda tidak akan dapat dengan mudah mengetahui alur kode karena dependensi disembunyikan dalam file XML.
sumber
Dua hal:
Misalnya, IntelliJ (edisi komersial) memiliki dukungan untuk memeriksa validitas konfigurasi Spring, dan akan menandai kesalahan seperti pelanggaran tipe dalam konfigurasi. Tanpa dukungan alat semacam itu, Anda tidak dapat memeriksa apakah konfigurasi tersebut valid sebelum menjalankan tes.
Ini adalah salah satu alasan mengapa pola 'cake' (seperti diketahui oleh komunitas Scala) adalah ide yang bagus: kabel antar komponen dapat diperiksa oleh pemeriksa tipe. Anda tidak memiliki manfaat dengan anotasi atau XML.
Kerangka kerja seperti Spring atau Guice membuatnya sulit untuk menentukan secara statis seperti apa grafik objek yang dibuat oleh kontainer. Meskipun mereka membuat grafik objek ketika wadah dimulai, mereka tidak menyediakan API bermanfaat yang menggambarkan grafik objek yang akan / akan dibuat.
sumber
Sepertinya manfaat yang seharusnya dari bahasa yang diketik secara statis berkurang secara signifikan ketika Anda terus-menerus menggunakan teknik untuk mengatasi pengetikan yang statis. Satu toko Java besar yang baru saja saya wawancarai memetakan dependensi build mereka dengan analisis kode statis ... yang harus mengurai semua file Spring agar menjadi efektif.
sumber
Ini dapat meningkatkan waktu memulai aplikasi karena wadah IoC harus menyelesaikan dependensi dengan cara yang benar dan kadang-kadang diperlukan untuk membuat beberapa iterasi.
sumber