Apa kerugian menggunakan Injeksi Ketergantungan? [Tutup]


336

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.


Mungkin ide yang baik untuk menentukan apakah kita seharusnya mendiskusikan DI sendiri atau jenis alat tertentu yang mendukungnya (berbasis XML atau tidak).
Jesse Millikan

5
perbedaannya adalah bahwa saya TIDAK mencari jawaban yang hanya berlaku untuk kerangka kerja tertentu, seperti "XML sucks". :) Saya mencari jawaban yang berlaku untuk konsep DI sebagaimana dijelaskan oleh Fowler di sini: martinfowler.com/articles/injection.html
Epaga

3
Saya melihat tidak ada kerugian sama sekali untuk DI secara umum (konstruktor, setter atau metode). Ini dipisahkan dan disederhanakan tanpa overhead.
Kissaki

2
@ Kississ terlepas dari setter menyuntikkan barang-barang. Terbaik untuk membuat objek yang dapat digunakan pada konstruksi. Jika Anda tidak percaya, coba tambahkan kolaborator lain ke objek dengan injeksi 'setter', Anda akan mendapatkan NPE pasti ....
time4tea

Jawaban:


209

Beberapa poin:

  • DI meningkatkan kompleksitas, biasanya dengan meningkatkan jumlah kelas karena tanggung jawab lebih dipisahkan, yang tidak selalu bermanfaat
  • Kode Anda akan (agak) digabungkan dengan kerangka kerja injeksi ketergantungan yang Anda gunakan (atau lebih umum bagaimana Anda memutuskan untuk menerapkan pola DI)
  • Wadah atau pendekatan yang melakukan penyelesaian jenis umumnya dikenakan penalti runtime ringan (sangat dapat diabaikan, tetapi ada di sana)

Secara umum, manfaat dari decoupling membuat setiap tugas lebih mudah untuk dibaca dan dipahami, tetapi meningkatkan kompleksitas mengatur tugas yang lebih kompleks.


72
- Pemisahan kelas mengurangi kompleksitas. Banyak kelas tidak membuat kompleks aplikasi. - Anda seharusnya hanya memiliki ketergantungan pada kerangka kerja DI Anda di root aplikasi.
Robert

91
Kita adalah manusia; memori jangka pendek kami terbatas dan tidak dapat menangani banyak <xxx> secara bersamaan. Itu sama untuk kelas seperti halnya untuk metode, fungsi, file, atau konstruksi apa pun yang Anda gunakan untuk mengembangkan program Anda.
Håvard S

45
@Havard S: Ya, tapi 5 kelas yang sangat kompleks tidak lebih sederhana dari 15 kelas sederhana. Cara untuk mengurangi kompleksitas adalah dengan mengurangi set kerja yang dibutuhkan oleh programmer yang bekerja pada kode - kompleks, kelas yang sangat saling tergantung tidak mencapai itu.
kyoryu

35
@kyoryu Saya pikir kita semua sepakat tentang itu. Perlu diingat bahwa kompleksitas tidak sama dengan kopling . Mengurangi kopling dapat meningkatkan kompleksitas. Saya benar-benar tidak mengatakan bahwa DI adalah hal yang buruk karena meningkatkan jumlah kelas, saya menyoroti potensi kerugian yang terkait dengannya. :)
Håvard S

96
+1 untuk "DI meningkatkan kompleksitas" Itu benar di DI dan di luar. (Hampir) setiap kali kita meningkatkan fleksibilitas, kita akan meningkatkan kompleksitas. Ini semua tentang keseimbangan, yang dimulai dengan mengetahui sisi positif dan buruknya. Ketika orang berkata, 'tidak ada kerugian,' itu adalah indikator pasti bahwa mereka belum sepenuhnya memahami hal itu.
Don Branson

183

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 ...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}

Saya memiliki ketergantungan, saya ingin mengekstrak dan menyuntikkan - teks "Hello World". Cukup mudah...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}

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.


3
Injeksi ketergantungan tidak memiliki kelemahan tersebut. Itu hanya mengubah cara Anda meneruskan dependensi ke objek, yang tidak menambah kompleksitas atau tidak fleksibel. Justru sebaliknya.
Kissaki

24
@ Kissaki - ya, itu mengubah cara Anda melewati dependensi Anda. Dan Anda harus menulis kode untuk menangani itu lewat dependensi. Dan sebelum melakukan itu, Anda harus menentukan dependensi mana yang harus dilewati, mendefinisikannya dengan cara yang masuk akal bagi seseorang yang tidak mengkode kode dependen, dll. Anda dapat menghindari dampak run-time (misalnya menggunakan parameter kebijakan untuk template di C ++), tapi itu masih kode yang harus kamu tulis dan pertahankan. Jika itu dibenarkan, itu adalah harga yang sangat kecil untuk hadiah besar, tetapi jika Anda berpura-pura tidak ada harga yang harus dibayar itu berarti Anda akan membayar harga itu ketika tidak ada keuntungan.
Steve314

6
@Kissaki - untuk ketidakfleksibelan, begitu Anda menentukan dependensi Anda, Anda terkunci di dalamnya - orang lain memiliki dependensi tertulis untuk disuntikkan sesuai dengan spesifikasi itu. Jika Anda memerlukan implementasi baru untuk kode dependen yang memerlukan dependensi yang sedikit berbeda - tangguh, Anda terkunci. Saatnya untuk mulai menulis beberapa adapter untuk dependensi itu (dan sedikit lebih banyak overhead dan lapisan abstraksi lain).
Steve314

Saya tidak yakin saya mengerti contoh ini, jika Anda mempertimbangkan kelebihan beban. Untuk menampilkan literal "Hello World", kami menggunakan tanda tangan (). Untuk menghasilkan string 8-bit, gunakan tanda tangan (char). Untuk menampilkan unicode, overload (wchar_t). Jelas, dalam hal ini, (wchar_t) adalah yang dipanggil orang lain di belakang layar. Tidak banyak penulisan ulang kode yang diperlukan di sana. Apakah saya melewatkan sesuatu?
ingredient_15939

2
@ ingredient_15939 - ya, ini adalah contoh mainan yang disederhanakan. Jika Anda benar-benar melakukan ini, Anda hanya akan menggunakan overloading daripada adaptor karena biaya menduplikasi kode kurang dari biaya adaptor. Tetapi perhatikan bahwa menggandakan kode pada umumnya adalah hal yang buruk, dan menggunakan adaptor untuk menghindari duplikasi kode adalah teknik lain yang baik (yang kadang-kadang dapat digunakan terlalu banyak dan di tempat yang salah).
Steve314

77

Inilah reaksi awal saya sendiri: Pada dasarnya kerugian yang sama dari pola apa pun.

  • butuh waktu untuk belajar
  • jika disalahpahami itu dapat menyebabkan lebih banyak kerusakan daripada kebaikan
  • jika dibawa ke ekstrem itu bisa lebih banyak bekerja daripada akan membenarkan manfaatnya

2
Mengapa perlu waktu untuk belajar? Saya pikir itu membuat hal-hal yang sederhana dibandingkan dengan ejb.
fastcodejava

8
Ini tampaknya bergelombang mengingat Anda tidak ingin diskusi subyektif. Saya menyarankan membangun (secara kolektif, jika perlu) contoh realistis untuk pemeriksaan. Membangun sampel tanpa DI akan menjadi titik awal yang baik. Lalu kita bisa menantangnya untuk perubahan persyaratan dan memeriksa dampaknya. (Omong-omong, ini sangat nyaman bagi saya untuk menyarankan, karena saya akan pergi tidur.)
Jesse Millikan

4
Ini benar-benar memerlukan beberapa contoh untuk mendukungnya, dan setelah mengajar puluhan orang DI, saya dapat memberi tahu Anda bahwa itu benar-benar konsep yang sangat sederhana untuk dipelajari dan mulai dipraktikkan.
chillitom

4
Saya tidak benar-benar mempertimbangkan DI suatu pola. Ini (ditambah dengan IoC) benar-benar lebih dari model pemrograman - jika Anda sepenuhnya mengikuti mereka, kode Anda terlihat jauh lebih seperti Aktor daripada pseudo-prosedural OO.
kyoryu

1
+1 Saya menggunakan Symfony 2, DI adalah seluruh kode pengguna, exausting hanya menggunakannya.
MGP

45

"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.


2
Tetapi tentu saja "downside" itu adalah karena sifat dari masalah yang kita selesaikan, memisahkannya sehingga kita dapat dengan mudah mengubah implementasi berarti tidak ada satu tempat untuk melihat, dan relevansi apa yang dapat dilihatnya? Satu-satunya kasus yang bisa saya pikirkan adalah dalam debugging dan lingkungan debugging harus dapat melangkah ke implementasi.
vickirk

3
Inilah sebabnya mengapa kata "downside" dalam tanda kutip :) Kopling longgar dan enkapsulasi kuat menghalangi "satu tempat untuk melihat segalanya," jenis menurut definisi. Lihat komentar saya tentang respons Havard S, jika Anda merasa bahwa saya menentang DI / IoC.
kyoryu

1
Saya setuju (saya bahkan memilih Anda). Saya hanya membuat titik.
vickirk

1
@ Vickirk: Kelemahan saya kira adalah sulit bagi manusia untuk memahaminya. Hal-hal yang memisahkan meningkatkan kompleksitas dalam arti bahwa lebih sulit bagi manusia untuk memahami, dan membutuhkan waktu lebih lama untuk dipahami sepenuhnya.
Bjarke Freund-Hansen

2
Sebaliknya, satu keuntungan dari DI adalah bahwa Anda dapat melihat konstruktor dari kelas individu, dan mengerjakan langsung kelas apa yang menjadi sandarannya (yaitu, kelas mana yang perlu melakukan tugasnya). Ini jauh lebih sulit untuk kode lain, karena kode dapat membuat kelas mau tak mau.
Contango

42

7
Saya ingin tahu, ketika seseorang meminta di sisi lain, mengapa jawaban dalam semangat "tidak ada" terangkat dan mereka yang berisi beberapa informasi yang terkait dengan pertanyaan tidak?
Gabriel Ščerbák

12
-1 karena saya tidak mencari koleksi tautan, saya mencari jawaban aktual: bagaimana dengan merangkum poin masing-masing artikel? Maka saya lebih baik memilih. Perhatikan bahwa artikel itu sendiri cukup menarik walaupun tampaknya dua yang pertama sebenarnya menghilangkan prasangka negatif dari DI?
Epaga

4
Saya bertanya-tanya apakah jawaban yang paling tervvotasikan turun juga, karena itu benar-benar tidak menjawab pertanyaan sama sekali imho :) Tentu saya tahu apa yang Anda tanyakan dan apa yang saya jawab, saya tidak meminta upvotes, saya hanya bingung mengapa saya jawabannya tidak membantu sementara tampaknya mengatakan DI downside adalah terlalu dingin membantu banyak.
Gabriel Ščerbák

41

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:

  • Kode bisa menjadi lebih sulit untuk dipahami. Injeksi ketergantungan dapat digunakan dengan sangat ... kreatif ... cara. Sebagai contoh, saya baru saja menemukan beberapa kode yang menggunakan anotasi khusus untuk menyuntikkan IOStreams tertentu (misalnya: @ Server1Stream, @ Server2Stream). Meskipun ini berhasil, dan saya akui memiliki keanggunan tertentu, itu membuat memahami injeksi Guice prasyarat untuk memahami kode.
  • Kurva belajar yang lebih tinggi saat belajar proyek. Ini terkait dengan poin 1. Untuk memahami bagaimana proyek yang menggunakan injeksi ketergantungan bekerja, Anda perlu memahami pola injeksi ketergantungan dan kerangka kerja tertentu. Ketika saya memulai pekerjaan saya saat ini, saya menghabiskan beberapa jam dengan bingung memahami apa yang dilakukan Guice di belakang layar.
  • Konstruktor menjadi besar. Meskipun ini sebagian besar dapat diselesaikan dengan konstruktor default atau pabrik.
  • Kesalahan bisa dikaburkan. Contoh terbaru saya tentang ini adalah saya memiliki tabrakan pada 2 nama bendera. Guice menelan kesalahan secara diam-diam dan salah satu dari bendera saya tidak diinisialisasi.
  • Kesalahan didorong ke run-time. Jika Anda mengkonfigurasi modul Guice Anda secara tidak benar (referensi melingkar, penjilidan yang buruk, ...) sebagian besar kesalahan tidak terungkap selama waktu kompilasi. Sebagai gantinya, kesalahan diekspos ketika program benar-benar dijalankan.

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.


14
Saya tidak mengerti mengapa orang tidak membuat lebih banyak "Kesalahan didorong untuk menjalankan-waktu" bagi saya itu adalah pemecah kesepakatan, pengetikan statis dan kompilasi kesalahan waktu adalah hadiah terbesar yang diberikan kepada pengembang, saya tidak akan membuang mereka pergi untuk apa pun
Richard Tingle

2
@RichardTingle, IMO selama aplikasi memulai modul DI akan diinisialisasi terlebih dahulu sehingga kesalahan konfigurasi dalam modul akan terlihat segera setelah aplikasi dimulai alih-alih setelah beberapa hari atau waktu. Dimungkinkan juga untuk memuat modul secara bertahap tetapi jika kita tetap berpegang pada semangat DI dengan membatasi pemuatan modul sebelum menginisialisasi logika aplikasi, kita dapat berhasil mengisolasi binding buruk ke awal aplikasi. Tetapi jika kita mengkonfigurasinya sebagai anti-locator layanan pola maka binding buruk itu pasti akan mengejutkan.
k4vin

@ RichardTingle Saya mengerti bahwa itu tidak akan pernah mirip dengan jaring pengaman yang disediakan oleh kompiler tetapi DI sebagai alat yang digunakan dengan benar seperti yang diceritakan dalam kotak kemudian kesalahan runtime terbatas pada inisialisasi aplikasi. Kemudian kita dapat melihat inisialisasi aplikasi sebagai semacam fase kompilasi untuk modul DI. Dalam pengalaman saya sebagian besar waktu jika aplikasi dimulai maka tidak akan ada binding buruk atau referensi yang salah di dalamnya. PS - Saya telah menggunakan NInject dengan C #
k4vin

@RichardTingle - Saya setuju dengan Anda, tetapi ini merupakan trade-off untuk mendapatkan kode yang digabungkan secara longgar, sehingga dapat diuji. Dan seperti kata k4vin, dependensi yang hilang ditemukan pada inisialisasi, dan menggunakan antarmuka masih akan membantu dengan kompilasi kesalahan waktu.
Andrei Epure

1
Saya akan menambahkan "Sulit untuk menjaga kode bersih" di daftar Anda. Anda tidak pernah tahu apakah Anda dapat menghapus registrasi sebelum Anda secara ekstensif menguji kode tanpa itu.
fernacolo

24

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.


1
Ya, saya suka istilah "kode ravioli"; Saya mengungkapkannya lebih panjang ketika saya berbicara tentang kelemahan DI. Namun, saya tidak dapat membayangkan mengembangkan kerangka kerja atau aplikasi nyata di Java tanpa DI.
Kapal Howard M. Lewis

13

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.

Misalkan Anda ingin merancang kampus perguruan tinggi. Anda harus mendelegasikan beberapa desain kepada siswa dan profesor, jika tidak gedung Fisika tidak akan berfungsi dengan baik untuk orang-orang fisika. Tidak ada arsitek yang cukup tahu tentang apa yang perlu dilakukan oleh orang-orang fisika itu sendiri. Tetapi Anda tidak dapat mendelegasikan desain setiap kamar kepada penghuninya, karena dengan begitu Anda akan mendapatkan tumpukan puing raksasa.

Bagaimana Anda dapat mendistribusikan tanggung jawab untuk desain melalui semua tingkatan hierarki besar, sambil tetap mempertahankan konsistensi dan harmoni desain keseluruhan? Ini adalah masalah desain arsitektur yang coba dipecahkan Alexander, tetapi juga masalah mendasar pengembangan 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.


13

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 ...

Public Class MyClass
    Private ReadOnly mExLogHandlerService As IExceptionLogHandlerService

    Public Sub New(exLogHandlerService As IExceptionLogHandlerService)
        Me.mExLogHandlerService = exLogHandlerService
    End Sub

     ...
End Class

... 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.


1
Saya mengerti apa yang Anda katakan, tapi saya pikir itu tidak adil untuk mengatakan "ketika Anda membuat implementasi ketergantungan, Anda harus mendesainnya dengan mengingat DI" - Saya pikir pernyataan yang lebih akurat adalah "DI bekerja terbaik ketika digunakan dengan kelas yang diimplementasikan dengan praktik terbaik mengenai biaya instantiation dan kurangnya efek samping atau kegagalan mode selama konstruksi ". Kasus terburuk, Anda selalu bisa menyuntikkan implementasi proksi malas yang menolak alokasi objek nyata hingga digunakan pertama kali.
Jolly Roger

1
Setiap kontainer IoC modern akan memungkinkan Anda untuk menentukan umur objek untuk jenis / antarmuka abstrak tertentu (selalu unik, tunggal, cakupan http, dll). Anda juga dapat memberikan metode / delegasi pabrik untuk membuat instantiate dengan malas menggunakan Fungsi <T> atau Malas <T>.
Dmitry S.

Spot on. Instansiasi ketergantungan tidak perlu biaya memori. Saya biasanya menggunakan "lazy-loading" dengan getter yang hanya membuat kelas ketika dipanggil. Anda dapat memberikan konstruktor default tanpa parameter serta konstruktor berikutnya yang menerima dependensi instantiated. Dalam kasus pertama, kelas yang mengimplementasikan IErrorLogger, misalnya, hanya dipakai dengan this.errorLogger.WriteError(ex)ketika kesalahan terjadi dalam pernyataan try / catch.
John Bonfardeci

12

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.


11

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.


5
Ctrl-shift-B dengan resharper akan membawa Anda implementasinya
adrianm

1
Tetapi sekali lagi apakah kita (di dunia yang ideal) tidak memiliki setidaknya 2 implementasi dari semuanya, yaitu setidaknya satu implementasi nyata dan satu tiruan untuk pengujian unit ;-)
vickirk

1
Di Eclipse jika Anda menahan Ctrl dan mengarahkan kursor ke kode doa metode, akan muncul menu untuk membuka Deklarasi atau Implementasi.
Sam Hasler

Jika Anda berada di definisi antarmuka, sorot nama metode dan tekan Ctrl + T untuk memunculkan hierarki jenis untuk metode itu - Anda akan melihat setiap implementor dari metode itu dalam hierarki jenis pohon.
qualidafial

10

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 doSomethingmetode.

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.


1
Masalah dengan berbasis konstruktor adalah Anda harus selalu menambahkan lebih banyak konstruktor ...
Miguel Ping

1
Ha ha. Jika Anda melakukan itu terlalu banyak, Anda akan segera melihat bahwa kode Anda jelek dan Anda akan mengubahnya. Selain itu, ctrl-f6 tidak terlalu sulit.
time4tea

Dan tentu saja kebalikannya juga benar: itu semua hanya cara fungsional kludgy menulis kelas sebagai penutupan untuk mendapatkan manfaat dari pemrograman OO
CurtainDog

Bertahun-tahun yang lalu saya membangun sistem objek menggunakan closure di Perl, jadi saya mengerti apa yang Anda katakan, tetapi merangkum data bukan manfaat unik dari pemrograman OO, jadi tidak jelas apa fitur bermanfaat dari OO ini yang akan sebanding kludgy dan kidal untuk mendapatkan dalam bahasa fungsional.
sanityinc

9

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!)


8
Jika Anda memiliki konstruktor jelek besar, mungkin kelas Anda terlalu besar dan Anda hanya perlu banyak ketergantungan?
Robert

4
Itu tentu saja kemungkinan saya bersedia menghibur! ... Sedihnya, saya tidak punya tim yang bisa saya lakukan tinjauan rekan. biola bermain dengan lembut di latar belakang, dan kemudian layar memudar menjadi hitam ...
James B

1
Seperti Mark Seeman sedih di atas "Ini bisa berbahaya bagi karir Anda" ...
Robert

Saya tidak memiliki petunjuk latar belakang yang bisa begitu ekspresif di sini: P
Anurag 9'10

@ Robert Saya mencoba mencari kutipannya, di mana dia mengatakan itu?
liang

8

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.


Poin yang bagus. Itu adalah kualitas buruk / pengiriman cepat vs kualitas baik / pengiriman lambat. Kelemahannya? DI bukanlah sihir, mengharapkannya adalah downside.
liang

5

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, ...:

  • Anda harus melatih tim Anda
  • Anda harus mengubah aplikasi Anda (yang termasuk risiko)

Secara umum Anda harus menginvestasikan waktu dan uang , di samping itu, tidak ada kerugian, sungguh!


3

Keterbacaan kode. Anda tidak akan dapat dengan mudah mengetahui alur kode karena dependensi disembunyikan dalam file XML.


9
Anda tidak perlu menggunakan file konfigurasi XML untuk injeksi dependensi - pilih wadah DI yang mendukung konfigurasi dalam kode.
Håvard S

8
Ketergantungan injeksi tidak selalu menyiratkan file XML. Sepertinya ini mungkin masih menjadi kasus di Jawa, tetapi dalam. NET kami mengabaikan kopling ini bertahun-tahun yang lalu.
Mark Seemann

6
Atau jangan gunakan wadah DI sama sekali.
Jesse Millikan

jika Anda tahu kode menggunakan DI, Anda dapat dengan mudah menganggap siapa yang menetapkan dependensi.
Bozho

Di Java, Google Guice tidak memerlukan file XML misalnya.
Epaga

2

Dua hal:

  • Mereka memerlukan dukungan alat tambahan untuk memeriksa apakah konfigurasi itu valid.

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.

  • Itu membuat analisis statis global program sangat sulit.

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.


1
Ini omong kosong mutlak. Ketergantungan injeksi adalah sebuah konsep, tidak memerlukan kerangka kerja yang rumit. Yang Anda butuhkan baru. Parit pegas dan semua omong kosong itu, maka alat Anda akan berfungsi dengan baik, ditambah Anda akan dapat memperbaiki kode Anda jauh lebih baik.
time4tea

Benar, poin bagus. Saya seharusnya lebih jelas bahwa saya berbicara tentang masalah dengan kerangka kerja DI dan bukan polanya. Mungkin saya melewatkan klarifikasi untuk pertanyaan ketika saya menjawab (dengan asumsi itu ada pada saat itu).
Martin Ellis

Ah. Dalam hal ini saya dengan senang hati menarik kekesalan saya. permintaan maaf.
time4tea

2

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.


1

Ini dapat meningkatkan waktu memulai aplikasi karena wadah IoC harus menyelesaikan dependensi dengan cara yang benar dan kadang-kadang diperlukan untuk membuat beberapa iterasi.


3
Hanya untuk memberi angka, sebuah wadah DI harus menyelesaikan ribuan dependensi per detik. ( codinginstinct.com/2008/05/... ) Wadah DI memungkinkan instantiasi yang ditangguhkan. Kinerja seharusnya (bahkan dalam aplikasi besar) tidak menjadi masalah. Atau setidaknya masalah kinerja harus diatasi dan tidak menjadi alasan untuk memutuskan terhadap IoC dan kerangka kerjanya.
Robert
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.