Kontainer IOC melanggar Prinsip OOP

51

Apa tujuan dari Kontainer IOC? Alasan gabungan untuk itu dapat disederhanakan sebagai berikut:

Saat menggunakan prinsip-prinsip Pengembangan OOP / SOLID, Injeksi Ketergantungan menjadi berantakan. Entah Anda memiliki titik masuk level atas yang mengelola dependensi untuk beberapa level di bawahnya dan melewati dependensi secara rekursif melalui konstruksi, atau Anda memiliki kode duplikat dalam pola pabrik / pembangun dan antarmuka yang membangun dependensi sesuai kebutuhan Anda.

Tidak ada OOP / SOLID cara untuk melakukan ini DAN memiliki kode yang sangat cantik.

Jika pernyataan sebelumnya itu benar, lalu bagaimana IOC Containers melakukannya? Sejauh yang saya tahu, mereka tidak menggunakan beberapa teknik yang tidak diketahui yang tidak dapat dilakukan dengan manual DI Jadi satu-satunya eksplorasi adalah bahwa IOC Containers melanggar Prinsip OOP / SOLID dengan menggunakan objek statis accessors pribadi.

Apakah IOC Containers melanggar prinsip-prinsip berikut di belakang layar? Ini adalah pertanyaan sebenarnya karena saya memiliki pemahaman yang baik, tetapi memiliki perasaan orang lain memiliki pemahaman yang lebih baik:

  1. Kontrol ruang lingkup . Lingkup adalah alasan untuk hampir setiap keputusan yang saya buat pada desain kode saya. Blok, Lokal, Modul, Statis / Global. Lingkup harus sangat eksplisit, sebanyak di level blok dan sesedikit mungkin di Statis. Anda harus melihat deklarasi, contoh, dan akhir siklus hidup. Saya mempercayai bahasa dan GC untuk mengelola ruang lingkup selama saya eksplisit dengannya. Dalam penelitian saya, saya menemukan bahwa Kontainer IOC mengatur sebagian besar atau semua dependensi sebagai Statis dan mengendalikannya melalui beberapa implementasi AOP di belakang layar. Jadi tidak ada yang transparan.
  2. Enkapsulasi . Apa tujuan enkapsulasi? Mengapa kita harus menjaga anggota pribadi begitu? Untuk alasan praktis, implementor API kami tidak dapat menghentikan implementasi dengan mengubah status (yang harus dikelola oleh kelas pemilik). Tetapi juga, karena alasan keamanan, suntikan tidak dapat terjadi yang mengambil alih negara anggota kami dan memintas validasi dan kontrol kelas. Jadi apa pun (kerangka kerja mengejek atau kerangka kerja IOC) yang entah bagaimana menyuntikkan kode sebelum mengkompilasi waktu untuk memungkinkan akses eksternal ke anggota pribadi sangat besar.
  3. Prinsip Tanggung Jawab Tunggal . Di permukaan, IOC Containers tampaknya membuat segalanya lebih bersih. Tetapi bayangkan bagaimana Anda akan mencapai hal yang sama tanpa kerangka kerja pembantu. Anda akan memiliki konstruktor dengan selusin dependensi yang dilewati. Itu tidak berarti menutupinya dengan Kontainer IOC, itu adalah hal yang baik! Ini adalah tanda untuk memfaktorkan ulang kode Anda dan mengikuti SRP.
  4. Buka / Tutup . Sama seperti SRP bukan Kelas-Saja (saya menerapkan SRP ke jalur tanggung jawab tunggal, apalagi metode). Open / Closed bukan hanya teori tingkat tinggi untuk tidak mengubah kode kelas. Ini adalah praktik memahami konfigurasi program Anda dan memiliki kendali atas apa yang diubah dan apa yang diperluas. Kontainer IOC dapat mengubah cara kerja kelas Anda sebagian karena:

    • Sebuah. Kode utama tidak membuat penentuan ketergantungan keluar, konfigurasi kerangka adalah.

    • b. Ruang lingkup dapat diubah pada waktu yang tidak dikontrol oleh anggota pemanggil, melainkan ditentukan secara eksternal oleh kerangka kerja statis.

Jadi konfigurasi kelas tidak benar-benar tertutup, itu mengubah sendiri berdasarkan konfigurasi alat pihak ketiga.

Alasan mengapa ini menjadi pertanyaan adalah karena saya belum tentu menguasai semua Kontainer IOC. Dan meskipun gagasan tentang IOC Container bagus, mereka tampaknya hanya façade yang menutupi implementasi yang buruk. Tetapi untuk mencapai kontrol Lingkup eksternal, akses anggota Privat, dan pemuatan Malas, banyak hal yang tidak sepele yang dipertanyakan harus terus berjalan. AOP itu hebat, tetapi cara itu dicapai melalui IOC Containers juga dipertanyakan.

Saya bisa mempercayai C # dan .NET GC untuk melakukan apa yang saya harapkan. Tapi saya tidak bisa menaruh kepercayaan yang sama pada alat pihak ketiga yang mengubah kode kompilasi saya untuk melakukan solusi untuk hal-hal yang saya tidak akan bisa lakukan secara manual.

EG: Kerangka Entitas dan ORM lainnya menciptakan objek yang sangat diketik dan memetakannya ke entitas basis data, serta menyediakan fungsionalitas dasar boiler-plate untuk melakukan CRUD. Siapa saja dapat membangun ORM mereka sendiri dan terus mengikuti Prinsip OOP / SOLID secara manual. Tapi kerangka itu membantu kita sehingga kita tidak perlu menemukan kembali roda setiap saat. Sedangkan Kontainer IOC, tampaknya, membantu kami dengan sengaja bekerja di sekitar Prinsip OOP / SOLID dan menutupinya.

Suamere
sumber
13
+1 Korelasi antara IOCdan Explicitness of codepersis hal yang saya miliki masalah dengan. Manual DI dapat dengan mudah menjadi tugas, tetapi setidaknya menempatkan alur program eksplisit mandiri di satu tempat - "Anda mendapatkan apa yang Anda lihat, Anda melihat apa yang Anda dapatkan". Sementara binding dan deklarasi kontainer IOC dapat dengan mudah menjadi program paralel tersembunyi sendiri.
Eugene Podskal
6
Bagaimana ini bahkan sebuah pertanyaan?
Stephen
8
Ini sepertinya lebih seperti posting blog daripada pertanyaan.
Bart van Ingen Schenau
4
Saya memiliki kategori internal untuk pertanyaan, "terlalu argumentatif", yang ofc bukan alasan penutupan formal tetapi saya membatalkan dan kadang-kadang bahkan memilih untuk menutup "bukan pertanyaan nyata". Ini memiliki dua aspek, dan pertanyaan ini hanya memenuhi yang pertama. (1) Pertanyaan menyatakan tesis dan bertanya apakah itu benar, (2) penanya merespons jawaban yang tidak setuju, mempertahankan posisi awal. Jawaban Matthew bertentangan dengan apa yang dinyatakan oleh pertanyaan itu, jadi kecuali si penanya merangkak di mana-mana, saya secara pribadi mengkategorikan ini sebagai pertanyaan dengan niat baik, meskipun dalam bentuk "Aku benar, bukan?"
Steve Jessop
3
@ BenAaronson Terima kasih, Ben. Saya mencoba untuk mengkomunikasikan apa yang telah saya pelajari melalui penelitian saya dan mendapatkan jawaban tentang sifat IOC Containers. Sebagai hasil dari penelitian saya, saya telah menemukan prinsip-prinsip ini menjadi rusak. Jawaban terbaik untuk pertanyaan saya akan mengkonfirmasi atau menolak bahwa IOC Containers dapat melakukan hal-hal yang tidak dapat kita lakukan secara manual tanpa melanggar Prinsip OOP / SOLID. Karena komentar Anda yang luar biasa, saya telah memperbarui pertanyaan saya dengan lebih banyak contoh.
Suamere

Jawaban:

35

Saya akan membahas poin-poin Anda secara numerik, tetapi pertama-tama, ada sesuatu yang harus Anda sangat berhati-hati: jangan mengacaukan bagaimana konsumen menggunakan perpustakaan dengan bagaimana perpustakaan diimplementasikan . Contoh bagus dari ini adalah Kerangka Entitas (yang Anda kutip sendiri sebagai perpustakaan yang baik) dan MVC ASP.NET. Keduanya melakukan banyak hal di bawah tenda dengan, misalnya, refleksi, yang sama sekali tidak dianggap desain yang baik jika Anda menyebarkannya melalui kode sehari-hari.

Perpustakaan ini sama sekali tidak "transparan" dalam cara kerjanya, atau apa yang mereka lakukan di belakang layar. Tapi itu bukan kerugian, karena mereka mendukung prinsip pemrograman yang baik di konsumen mereka . Jadi, setiap kali Anda berbicara tentang perpustakaan seperti ini, ingatlah bahwa sebagai konsumen perpustakaan, bukan tugas Anda untuk mengkhawatirkan implementasi atau pemeliharaannya. Anda hanya perlu khawatir tentang bagaimana ini membantu atau menghalangi kode yang Anda tulis yang menggunakan perpustakaan. Jangan mengacaukan konsep-konsep ini!

Jadi, untuk melewati poin:

  1. Segera kami mencapai apa yang hanya bisa saya asumsikan adalah contoh di atas. Anda mengatakan bahwa wadah IOC mengatur sebagian besar dependensi sebagai statis. Yah, mungkin beberapa detail implementasi tentang cara kerjanya termasuk penyimpanan statis (meskipun mengingat bahwa mereka cenderung memiliki instance objek seperti Ninject IKernelsebagai penyimpanan inti mereka, saya ragu bahkan itu). Tapi ini bukan urusanmu!

    Sebenarnya, wadah IoC sama eksplisitnya dengan ruang lingkup seperti injeksi ketergantungan orang miskin. (Aku akan terus membandingkan kontainer IoC dengan DI orang miskin karena membandingkannya dengan tidak ada DI sama sekali tidak adil dan membingungkan. Dan, untuk lebih jelasnya, aku tidak menggunakan "DI orang miskin" sebagai penghinaan.)

    Di DI orang miskin, Anda akan instantiate ketergantungan secara manual, lalu menyuntikkannya ke kelas yang membutuhkannya. Pada titik di mana Anda membangun ketergantungan, Anda akan memilih apa yang akan dilakukan dengan itu- menyimpannya dalam variabel cakupan lokal, variabel kelas, variabel statis, tidak menyimpannya sama sekali, apa pun. Anda bisa memberikan contoh yang sama ke banyak kelas, atau Anda bisa membuat yang baru untuk setiap kelas. Masa bodo. Intinya adalah, untuk melihat mana yang terjadi, Anda melihat ke titik - mungkin di dekat root aplikasi - tempat ketergantungan dibuat.

    Sekarang bagaimana dengan wadah IOC? Nah, Anda melakukan hal yang persis sama! Sekali lagi, akan dengan terminologi Ninject, Anda melihat di mana mengikat diatur, dan mencari apakah itu mengatakan sesuatu seperti InTransientScope, InSingletonScope, atau apa pun. Jika ada hal ini berpotensi lebih jelas, karena Anda memiliki kode di sana yang menyatakan ruang lingkupnya, daripada harus melihat melalui metode untuk melacak apa yang terjadi pada beberapa objek (objek dapat dicakup ke blok, tetapi apakah itu digunakan beberapa kali dalam hal itu blok atau hanya sekali, misalnya). Jadi mungkin Anda ditolak oleh gagasan harus menggunakan fitur pada wadah IoC daripada fitur bahasa mentah untuk menentukan ruang lingkup, tetapi selama Anda mempercayai perpustakaan IoC Anda - yang Anda harus! - tidak ada kerugian nyata untuk itu .

  2. Saya masih tidak benar-benar tahu apa yang Anda bicarakan di sini. Apakah wadah IoC melihat properti pribadi sebagai bagian dari implementasi internal mereka? Saya tidak tahu mengapa mereka melakukannya, tetapi sekali lagi, jika mereka melakukannya, bukan urusan Anda bagaimana perpustakaan yang Anda gunakan diimplementasikan.

    Atau, mungkin, mereka mengekspos kemampuan seperti menyuntikkan ke setter pribadi? Jujur saya belum pernah menemukan ini, dan saya ragu apakah ini benar-benar fitur umum. Tetapi bahkan jika itu ada, itu adalah kasus sederhana dari alat yang dapat disalahgunakan. Ingat, bahkan tanpa wadah IoC, hanya beberapa baris kode Refleksi untuk mengakses dan memodifikasi properti pribadi. Ini adalah sesuatu yang hampir tidak boleh Anda lakukan, tetapi itu tidak berarti. NET buruk untuk mengekspos kemampuan. Jika seseorang dengan jelas dan liar menyalahgunakan alat, itu kesalahan orang itu, bukan alat itu.

  3. Poin pamungkas di sini mirip dengan 2. Sebagian besar waktu, kontainer IOC menggunakan konstruktor! Injeksi setter ditawarkan untuk keadaan yang sangat spesifik di mana, karena alasan tertentu, injeksi konstruktor tidak dapat digunakan. Siapa pun yang menggunakan injeksi setter sepanjang waktu untuk menutupi berapa banyak dependensi yang dilewatkan secara masif meninggalkan alat. Itu BUKAN kesalahan alat, itu milik mereka.

    Sekarang, jika ini adalah kesalahan yang sangat mudah untuk dibuat dengan tidak bersalah, dan salah satu yang didorong oleh wadah IoC, maka oke, mungkin Anda akan ada benarnya. Itu seperti membuat setiap anggota kelas saya menjadi publik lalu menyalahkan orang lain ketika mereka memodifikasi hal-hal yang seharusnya tidak mereka lakukan, bukan? Tetapi siapa pun yang menggunakan injeksi setter untuk menutupi pelanggaran SRP dengan sengaja mengabaikan atau sama sekali tidak mengetahui prinsip-prinsip desain dasar. Tidak masuk akal untuk menyalahkan ini di wadah IoC.

    Itu terutama benar karena itu juga sesuatu yang dapat Anda lakukan dengan mudah dengan DI orang miskin:

    var myObject 
       = new MyTerriblyLargeObject { DependencyA = new Thing(), DependencyB = new Widget(), Dependency C = new Repository(), ... };
    

    Jadi sungguh, kekhawatiran ini tampaknya benar-benar ortogonal apakah wadah IOC digunakan atau tidak.

  4. Mengubah cara kerja kelas Anda bukanlah pelanggaran OCP. Jika ya, maka semua inversi ketergantungan akan mendorong pelanggaran OCP. Jika ini masalahnya, keduanya tidak akan berada dalam akronim SOLID yang sama!

    Lebih lanjut, baik poin a) maupun b) hampir tidak ada hubungannya dengan OCP. Saya bahkan tidak tahu bagaimana menjawabnya sehubungan dengan OCP.

    Satu-satunya hal yang bisa saya tebak adalah bahwa Anda pikir OCP ada hubungannya dengan perilaku yang tidak diubah saat runtime, atau tentang di mana dalam kode siklus hidup dependensi dikendalikan. Ini bukan. OCP adalah tentang tidak harus mengubah kode yang ada ketika persyaratan ditambahkan atau diubah. Ini semua tentang menulis kode, bukan tentang bagaimana kode yang sudah Anda tulis direkatkan (meskipun, tentu saja, kopling longgar adalah bagian penting untuk mencapai OCP).

Dan satu hal terakhir yang Anda katakan:

Tapi saya tidak bisa menaruh kepercayaan yang sama pada alat pihak ketiga yang mengubah kode kompilasi saya untuk melakukan solusi untuk hal-hal yang saya tidak akan bisa lakukan secara manual.

Ya kamu bisa . Sama sekali tidak ada alasan bagi Anda untuk berpikir bahwa alat-alat ini - yang diandalkan oleh sejumlah besar proyek - lebih buggy atau rentan terhadap perilaku yang tidak terduga daripada perpustakaan pihak ketiga lainnya.

Tambahan

Saya hanya melihat paragraf intro Anda bisa menggunakan beberapa pengalamatan juga. Anda dengan sinis mengatakan bahwa wadah IoC "tidak menggunakan beberapa teknik rahasia yang belum pernah kami dengar" untuk menghindari kode yang berantakan dan rawan duplikasi untuk membuat grafik ketergantungan. Dan Anda benar, apa yang mereka lakukan sebenarnya menangani hal-hal ini dengan teknik dasar yang sama seperti yang selalu dilakukan oleh para programmer.

Biarkan saya berbicara dengan Anda melalui skenario hipotetis. Anda, sebagai programmer, membuat aplikasi besar, dan pada titik masuk, tempat Anda membuat grafik objek, Anda melihat ada kode yang cukup berantakan. Ada beberapa kelas yang digunakan berulang kali, dan setiap kali Anda membangun salah satu dari itu Anda harus membangun seluruh rantai ketergantungan di bawahnya lagi. Selain itu, Anda tidak memiliki cara ekspresif untuk mendeklarasikan atau mengendalikan siklus hidup dependensi, kecuali dengan kode khusus untuk masing-masingnya. Kode Anda tidak terstruktur dan penuh pengulangan. Ini adalah kekacauan yang Anda bicarakan di paragraf intro Anda.

Jadi pertama, Anda mulai sedikit refactor - di mana beberapa kode berulang cukup terstruktur Anda menariknya ke dalam metode pembantu, dan seterusnya. Tapi kemudian Anda mulai berpikir- ini masalah yang saya mungkin bisa mengatasi dalam umum akal, salah satu yang tidak spesifik untuk proyek tertentu tetapi bisa membantu Anda dalam semua proyek masa depan Anda?

Jadi Anda duduk, dan memikirkannya, dan memutuskan bahwa harus ada kelas yang dapat menyelesaikan dependensi. Dan Anda membuat sketsa metode publik apa yang dibutuhkan:

void Bind(Type interfaceType, Type concreteType, bool singleton);
T Resolve<T>();

Bindmengatakan "di mana Anda melihat argumen konstruktor tipe interfaceType, berikan contoh concreteType". singletonParameter tambahan mengatakan apakah akan menggunakan instance yang sama concreteTypesetiap kali, atau selalu membuat yang baru.

Resolvehanya akan mencoba untuk membangun Tdengan konstruktor apa pun yang dapat ditemukan argumen siapa semua jenis yang sebelumnya terikat. Itu juga bisa menyebut dirinya secara rekursif untuk menyelesaikan dependensi sepenuhnya. Jika itu tidak dapat menyelesaikan sebuah instance karena tidak semuanya telah diikat, ia mengeluarkan pengecualian.

Anda dapat mencoba menerapkan ini sendiri, dan Anda akan menemukan Anda perlu sedikit refleksi, dan beberapa caching untuk binding mana singletonbenar, tetapi tentu saja tidak ada yang drastis atau mengerikan. Dan begitu Anda selesai - voila , Anda memiliki inti dari wadah IoC Anda sendiri! Apakah itu benar-benar menakutkan? Satu-satunya perbedaan nyata antara ini dan Ninject atau StructureMap atau Castle Windsor atau apa pun yang Anda inginkan adalah bahwa mereka memiliki lebih banyak fungsi untuk menutupi kasus penggunaan (banyak!) Di mana versi dasar ini tidak akan cukup. Tetapi pada intinya, apa yang Anda miliki adalah esensi dari wadah IoC.

Ben Aaronson
sumber
Ben terima kasih. Jujur saya hanya bekerja dengan IOC Containers selama sekitar satu tahun, dan meskipun saya tidak melakukannya, tutorial dan teman-teman di sekitar saya benar-benar fokus pada injeksi properti (termasuk pribadi). Ini gila, dan memunculkan semua poin yang saya buat. Tetapi bahkan jika orang lain berada dalam situasi ini, saya pikir hal terbaik yang dapat mereka lakukan adalah belajar lebih banyak tentang bagaimana IOC Containers harus bekerja untuk mengikuti prinsip, dan tunjukkan pemecah prinsip ini dalam ulasan kode. Namun ... (lanjutan)
Suamere
Kekhawatiran terbesar saya belum tentu tercantum dalam Prinsip OOP / SOLID, dan itu adalah ruang lingkup kejelasan. Saya masih kita melempar pemahaman Blok, Lokal, Modul, dan tingkat Global keluar jendela untuk kata kunci. Seluruh gagasan untuk menggunakan blok, dan deklarasi menentukan kapan sesuatu jatuh dari ruang lingkup atau dibuang sangat besar bagi saya, dan saya pikir itu adalah bagian paling menakutkan dari Kontainer IOC bagi saya, untuk menggantikan bagian yang sangat mendasar dari pengembangan dengan kata kunci ekstensi .
Suamere
Jadi saya menandai jawaban Anda sebagai jawaban karena itu benar-benar mengatasi inti dari ini. Dan cara saya membacanya adalah Anda hanya harus mempercayai apa yang dilakukan IOC Containers di balik selimut karena semua orang melakukannya. Dan sejauh celah untuk penyalahgunaan mungkin dan cara elegan menutupinya, itu untuk toko dan ulasan kode untuk khawatir tentang dan mereka harus melakukan uji tuntas mereka untuk mengikuti prinsip-prinsip pembangunan yang baik.
Suamere
2
Sehubungan dengan 4, Injeksi Ketergantungan tidak dalam akronim SOLID . 'D' = 'Prinsip Pembalikan Ketergantungan', yang merupakan konsep yang sama sekali tidak terkait.
astreltsov
@astr Poin bagus. Saya telah mengganti "injeksi" dengan "inversi" karena saya pikir itulah yang saya maksud untuk menulis. Itu masih sedikit penyederhanaan yang berlebihan karena inversi ketergantungan tidak selalu menyiratkan beberapa implementasi konkret, tapi saya pikir maknanya cukup jelas. Akan berbahaya untuk bergantung pada abstraksi polimorfik jika mematikan implementasi adalah pelanggaran OCP.
Ben Aaronson
27

Bukankah wadah IOC melanggar banyak prinsip ini?

Dalam teori? Tidak. Wadah IoC hanyalah alat. Anda dapat memiliki objek yang memiliki tanggung jawab tunggal, antarmuka yang terpisah, tidak dapat mengubah perilakunya setelah ditulis, dll.

Dalam praktek? Benar.

Maksudku, saya pernah mendengar tidak sedikit programmer mengatakan bahwa mereka menggunakan wadah IoC khusus untuk memungkinkan Anda menggunakan beberapa konfigurasi untuk mengubah perilaku sistem - melanggar Prinsip Terbuka / Tertutup. Dulu ada lelucon yang dibuat tentang pengkodean dalam XML karena 90% dari pekerjaan mereka memperbaiki konfigurasi Spring. Dan Anda tentu benar bahwa menyembunyikan dependensi (yang memang diakui, tidak semua wadah IOC melakukannya) adalah cara cepat dan mudah untuk membuat kode yang sangat berpasangan dan sangat rapuh.

Mari kita melihatnya dengan cara yang berbeda. Mari kita pertimbangkan kelas yang sangat mendasar yang memiliki ketergantungan tunggal, dasar. Itu tergantung pada Action, delegasi atau functor yang tidak mengambil parameter dan mengembalikan void. Apa tanggung jawab kelas ini? Anda tidak tahu. Saya dapat menyebutkan ketergantungan itu dengan sesuatu yang jelas seperti CleanUpActionyang membuat Anda berpikir itu melakukan apa yang Anda inginkan. Begitu IoC mulai berperan, ia menjadi apa saja . Siapa pun dapat masuk ke konfigurasi dan memodifikasi perangkat lunak Anda untuk melakukan banyak hal sewenang-wenang.

"Apa bedanya dengan memberikan Actionkepada konstruktor?" Anda bertanya. Ini berbeda karena Anda tidak dapat mengubah apa yang diteruskan ke konstruktor setelah perangkat lunak digunakan (atau saat runtime!). Ini berbeda karena tes unit Anda (atau tes unit konsumen Anda) telah membahas skenario di mana delegasi dilewatkan ke konstruktor.

"Tapi itu contoh palsu! Barang-barangku tidak memungkinkan perubahan perilaku!" Anda berseru. Maaf untuk memberitahumu, tetapi itu benar. Kecuali jika antarmuka yang Anda suntikkan hanya data. Dan antarmuka Anda bukan hanya data, karena jika itu hanya data, Anda akan membangun objek sederhana dan menggunakannya. Segera setelah Anda memiliki metode virtual di titik injeksi Anda, kontrak kelas Anda menggunakan IoC adalah "Saya bisa melakukan apa saja ".

"Apa bedanya dengan menggunakan skrip untuk mengendalikan sesuatu? Itu tidak apa-apa untuk dilakukan." beberapa dari Anda bertanya. Itu tidak berbeda. Dari perspektif desain program, tidak ada perbedaan. Anda memanggil program Anda untuk menjalankan kode arbitrer. Dan tentu saja, itu sudah dilakukan dalam game sejak lama. Ini dilakukan dalam banyak alat administrasi sistem. Apakah itu OOP? Tidak juga.

Tidak ada alasan untuk keberadaan mereka saat ini. Kontainer IoC memiliki satu tujuan yang kuat - untuk merekatkan ketergantungan Anda ketika Anda memiliki begitu banyak kombinasi, atau mereka bergerak begitu cepat sehingga tidak praktis untuk membuat dependensi itu eksplisit. Jadi sangat sedikit bisnis yang benar-benar sesuai dengan skenario itu.

Telastyn
sumber
3
Baik. Dan banyak toko akan mengklaim produk mereka sangat kompleks sehingga mereka masuk dalam kategori itu, padahal sebenarnya tidak. Seperti halnya kebenaran dengan banyak skenario.
Suamere
12
Tapi IoC sangat bertolak belakang dengan apa yang Anda katakan. Ini mempekerjakan Open / Kedekatan dari program. Jika Anda dapat mengubah perilaku seluruh program tanpa harus mengubah kode itu sendiri.
Euforia
6
@Euphoric - terbuka untuk ekstensi, ditutup untuk modifikasi. Jika Anda dapat mengubah perilaku seluruh program, itu tidak ditutup untuk modifikasi, bukan? Konfigurasi IoC masih merupakan perangkat lunak Anda, masih merupakan kode yang digunakan oleh kelas Anda untuk melakukan pekerjaan - bahkan jika itu dalam XML dan bukan Java atau C # atau apa pun.
Telastyn
4
@Telastyn "Tutup untuk modifikasi" berarti bahwa tidak perlu mengubah (memodifikasi) kode, untuk mengubah perilaku keseluruhan. Dengan konfigurasi eksternal apa pun, Anda bisa mengarahkannya ke dll baru dan memiliki perilaku perubahan keseluruhan.
Euforia
12
@ Telastyn Bukan itu yang dikatakan prinsip terbuka / tertutup. Kalau tidak, metode yang mengambil parameter akan melanggar OCP karena saya bisa "memodifikasi" perilakunya dengan memberikan parameter yang berbeda. Demikian juga di bawah versi OCP Anda, itu akan bertentangan langsung dengan DI, yang akan membuatnya agak aneh karena keduanya muncul dalam singkatan SOLID.
Ben Aaronson
14

Apakah menggunakan wadah IOC harus melanggar prinsip OOP / SOLID? Tidak ada .

Bisakah Anda menggunakan wadah IoC sedemikian rupa sehingga melanggar prinsip OOP / SOLID? Ya .

Kontainer IoC hanyalah kerangka kerja untuk mengelola dependensi dalam aplikasi Anda.

Anda menyatakan injeksi Malas, setter properti, dan beberapa fitur lain yang belum saya rasa perlu digunakan, yang saya setujui dapat memberi Anda desain yang kurang optimal. Namun, penggunaan fitur-fitur spesifik ini disebabkan oleh keterbatasan dalam teknologi tempat Anda menerapkan wadah IoC.

Misalnya, ASP.NET WebForms, Anda diharuskan untuk menggunakan injeksi properti karena tidak mungkin untuk instantiate a Page. Ini dapat dimengerti karena WebForms tidak pernah dirancang dengan paradigma OOP yang ketat dalam pikiran.

ASP.NET MVC di sisi lain, sangat mungkin untuk menggunakan wadah IoC menggunakan injeksi konstruktor ramah yang sangat OOP / SOLID.

Dalam skenario ini (menggunakan injeksi konstruktor), saya percaya ini mempromosikan prinsip SOLID karena alasan berikut:

  • Prinsip Tanggung Jawab Tunggal - Memiliki banyak kelas yang lebih kecil memungkinkan kelas-kelas ini menjadi sangat spesifik dan memiliki satu alasan untuk keberadaannya. Menggunakan wadah IoC membuat mengaturnya lebih mudah.

  • Buka / Tutup - Di sinilah menggunakan wadah IoC benar-benar bersinar, Anda dapat memperluas fungsionalitas kelas dengan cara menyuntikkan dependensi yang berbeda. Memiliki dependensi yang Anda suntikkan memungkinkan Anda mengubah perilaku kelas itu. Contoh yang baik adalah mengubah kelas yang Anda suntikkan dengan menulis dekorator untuk mengatakan menambahkan caching atau masuk. Anda benar-benar dapat mengubah implementasi dependensi Anda jika ditulis dalam tingkat abstraksi yang sesuai.

  • Prinsip Pergantian Liskov - Tidak terlalu relevan

  • Prinsip Segregasi Antarmuka - Anda dapat menetapkan banyak antarmuka ke waktu konkret tunggal jika Anda mau, setiap wadah IoC seharga sebutir garam akan dengan mudah melakukan ini.

  • Dependency Inversion - Container IoC memungkinkan Anda melakukan inversi ketergantungan dengan mudah.

Anda mendefinisikan banyak dependensi dan mendeklarasikan hubungan dan cakupan, dan bing bang boom: Anda secara ajaib memiliki beberapa addon yang mengubah kode atau IL Anda di beberapa titik dan membuat semuanya berfungsi. Itu tidak eksplisit atau transparan.

Baik eksplisit dan transparan, Anda perlu memberi tahu wadah IoC Anda cara memasang dependensi dan cara mengelola objek seumur hidup. Ini bisa dengan konvensi, dengan file konfigurasi, atau dengan kode yang ditulis dalam aplikasi utama sistem Anda.

Saya setuju bahwa ketika saya menulis atau membaca kode menggunakan wadah IOC, itu menghilangkan beberapa kode yang sedikit tidak berlaku.

Ini adalah alasan utama OOP / SOLID, untuk membuat sistem dapat dikelola. Menggunakan wadah IoC sejalan dengan tujuan itu.

Penulis kelas itu mengatakan, "Jika kita tidak menggunakan Kontainer IOC, Konstruktor akan memiliki selusin parameter !!! Itu mengerikan!"

Kelas dengan 12 dependensi mungkin merupakan pelanggaran SRP apa pun konteksnya Anda menggunakannya.

Matius
sumber
2
Jawaban yang sangat bagus. Selain itu, saya pikir semua wadah IOC utama juga mendukung AOP, yang dapat banyak membantu dengan OCP. Untuk masalah lintas sektoral, AOP biasanya rute yang lebih ramah-OCP daripada Dekorator.
Ben Aaronson
2
@ BenAaronson Mengingat bahwa AOP menyuntikkan kode ke dalam kelas, saya tidak akan menyebutnya lebih ramah-OCP. Anda dengan paksa membuka dan mengubah kelas saat kompilasi / runtime.
Doval
1
@Doval Saya tidak melihat bagaimana cara menyuntikkan kode ke dalam kelas. Catatan Saya berbicara tentang gaya Castle DynamicProxy, di mana Anda memiliki pencegat, bukan gaya tenun IL. Tapi pencegat pada dasarnya hanya dekorator yang menggunakan beberapa refleksi sehingga tidak harus berpasangan dengan antarmuka tertentu.
Ben Aaronson
"Pembalikan Ketergantungan - wadah IoC memungkinkan Anda melakukan inversi ketergantungan dengan mudah" - tidak, mereka hanya memanfaatkan sesuatu yang bermanfaat apakah Anda memiliki IoC atau tidak. Sama dengan prinsip lainnya.
Den
Saya benar-benar tidak mendapatkan logika penjelasan bahwa ada sesuatu yang tidak buruk berdasarkan fakta, bahwa itu adalah kerangka kerja. ServiceLocators dibuat sebagai kerangka kerja juga dan mereka dianggap sebagai buruk :).
ipavlu
5

Jangan wadah IOC baik istirahat, dan memungkinkan coders untuk istirahat, banyak prinsip OOP / SOLID?

Kata statickunci dalam C # memungkinkan pengembang untuk melanggar banyak prinsip OOP / SOLID tetapi masih merupakan alat yang valid dalam keadaan yang tepat.

Kontainer IoC memungkinkan kode terstruktur dengan baik dan mengurangi beban kognitif pada pengembang. Sebuah wadah IoC dapat digunakan untuk membuat prinsip-prinsip SOLID berikut jauh lebih mudah. Ini bisa disalahgunakan, tentu saja, tetapi jika kita mengecualikan penggunaan alat apa pun yang dapat disalahgunakan, kita harus menyerah pemrograman sama sekali.

Jadi, sementara kata-kata kasar ini memunculkan beberapa poin valid tentang kode yang ditulis dengan buruk, itu bukan pertanyaan dan tidak terlalu berguna.

Stephen
sumber
Ini adalah pemahaman saya tentang IOC Containers bahwa untuk mengelola ruang lingkup, kemalasan, dan aksesibilitas, itu mengubah kode yang dikompilasi untuk melakukan hal-hal yang melanggar OOP / SOLID, tetapi menyembunyikan implementasi itu dalam kerangka kerja yang bagus. Tapi ya, itu juga membuka pintu lain untuk penggunaan yang salah. Jawaban terbaik untuk pertanyaan saya adalah beberapa klarifikasi tentang bagaimana IOC Containers dilaksanakan yang menambah atau meniadakan penelitian saya sendiri.
Suamere
2
@Suamere - Saya pikir Anda mungkin menggunakan definisi IOC yang terlalu luas. Yang saya gunakan tidak mengubah kode yang dikompilasi. Ada banyak alat non-IOC lainnya yang juga memodifikasi kode kompilasi. Mungkin judul Anda harus lebih seperti: "Apakah memodifikasi kode yang dikompilasi ...".
Cerad
@Suamere Saya tidak berpengalaman dengan IoC, tapi saya tidak pernah mendengar tentang IoC yang mengubah kode kompilasi. Tolong berikan informasi tentang platform / bahasa / kerangka apa yang Anda bicarakan.
Euforia
1
"Wadah IoC dapat digunakan untuk membuat prinsip SOLID berikut jauh lebih mudah." - bisakah Anda menjelaskannya? Menjelaskan betapa spesifiknya itu membantu setiap prinsip akan sangat membantu. Dan tentu saja meningkatkan sesuatu tidak sama dengan membantu sesuatu terjadi (misalnya DI).
Den
1
@ Euphoric Saya tidak tahu tentang .net frameworks yang dibicarakan oleh suamiere, tetapi Spring tentu saja melakukan modifikasi kode di bawah kap. Contoh yang paling jelas adalah dukungannya untuk injeksi berbasis metode (yang menimpa metode abstrak di kelas Anda untuk mengembalikan dependensi yang dikelolanya). Itu juga membuat penggunaan luas proksi yang dihasilkan secara otomatis untuk membungkus kode Anda untuk memberikan perilaku tambahan (misalnya untuk mengelola transaksi secara otomatis). Namun, banyak dari ini sebenarnya tidak terkait dengan perannya sebagai kerangka kerja DI.
Jules
3

Saya melihat banyak kesalahan dan kesalahpahaman dalam pertanyaan Anda. Yang utama pertama adalah bahwa Anda kehilangan perbedaan antara injeksi properti / bidang, injeksi konstruktor, dan pencari layanan. Ada banyak diskusi (mis. Flamewars) tentang cara yang tepat untuk melakukan sesuatu. Dalam pertanyaan Anda, Anda tampaknya menganggap hanya injeksi properti dan mengabaikan injeksi konstruktor.

Hal kedua adalah Anda berasumsi bahwa wadah IoC mempengaruhi desain kode Anda. Mereka tidak, atau setidaknya, seharusnya tidak perlu mengubah desain kode Anda jika Anda menggunakan IOC. Masalah Anda dengan kelas dengan banyak konstruktor adalah kasus IoC menyembunyikan masalah nyata dengan kode Anda (kemungkinan besar kelas yang melanggar SRP) dan dependensi tersembunyi adalah salah satu alasan mengapa orang tidak menyarankan penggunaan injeksi properti dan pencari lokasi.

Saya tidak mengerti dari mana masalah dengan prinsip buka-tutup ini, jadi saya tidak akan mengomentarinya. Tetapi Anda kehilangan satu bagian dari SOLID, yang memiliki pengaruh besar pada ini: Prinsip Ketergantungan Pembalikan. Jika Anda mengikuti DIP, Anda akan menemukan, bahwa membalik dependensi memperkenalkan abstraksi, yang implementasinya perlu diselesaikan ketika instance kelas dibuat. Dengan pendekatan ini, Anda akan berakhir dengan banyak kelas, yang membutuhkan beberapa antarmuka untuk direalisasikan dan disediakan pada konstruksi agar berfungsi dengan baik. Jika Anda kemudian memberikan dependensi itu dengan tangan, Anda harus melakukan banyak pekerjaan tambahan. Kontainer IoC memudahkan untuk melakukan pekerjaan ini dan bahkan memungkinkan Anda untuk mengubahnya tanpa harus mengkompilasi ulang program.

Jadi, jawaban untuk pertanyaan Anda adalah tidak. Kontainer IoC tidak melanggar prinsip-prinsip desain OOP. Sebaliknya, mereka memecahkan masalah yang disebabkan oleh mengikuti prinsip-prinsip SOLID (terutama prinsip Ketergantungan-Inversi).

Euforia
sumber
Sudah mencoba menerapkan IoC (Autofac) ke proyek non-sepele baru-baru ini. Menyadari saya harus melakukan pekerjaan yang sama dengan konstruksi non-bahasa (baru () vs API): menentukan apa yang harus diselesaikan untuk antarmuka dan apa yang harus diselesaikan ke kelas konkret, menentukan apa yang harus menjadi contoh dan apa yang harus menjadi singleton, memastikan injeksi properti berfungsi dengan baik untuk mengurangi ketergantungan melingkar. Memutuskan untuk meninggalkan. Bekerja dengan baik dengan Pengendali di ASP MVC.
Den
@Den Proyek Anda tidak jelas dirancang dengan pertimbangan Ketergantungan. Dan tidak pernah saya katakan bahwa menggunakan IoC itu sepele.
Euforia
Sebenarnya, hal itu - saya menggunakan Ketergantungan Pembalikan sepanjang dari awal. IoC mempengaruhi desain di luar itu adalah kekhawatiran terbesar saya. Misalnya mengapa saya menggunakan antarmuka di mana saja hanya untuk membuat IoC lebih sederhana? Lihat juga komentar teratas pada pertanyaan.
Den
@ Apakah Anda secara teknis tidak "perlu" menggunakan antarmuka. Antarmuka membantu dengan mengejek untuk pengujian unit. Anda juga dapat menandai hal-hal yang perlu Anda tiru virtual.
Fred