Saya menggunakan Unity di C # untuk injeksi dependensi, tetapi pertanyaannya harus berlaku untuk bahasa dan kerangka kerja apa pun yang menggunakan injeksi dependensi.
Saya mencoba mengikuti prinsip SOLID dan karena itu saya mendapat banyak abstraksi. Tapi sekarang saya bertanya-tanya apakah ada praktik terbaik untuk berapa banyak suntikan yang harus disuntikkan satu kelas?
Sebagai contoh, saya memiliki repositori dengan 9 suntikan. Apakah ini sulit dibaca untuk pengembang lain?
Suntikan memiliki tanggung jawab berikut:
- IDbContextFactory - membuat konteks untuk database.
- IMapper - Memetakan dari entitas ke model domain.
- IClock - Abstracts DateTime. Sekarang dapat membantu dengan unit test.
- IPerformanceFactory - mengukur waktu eksekusi untuk metode tertentu.
- ILog - Log4net untuk logging.
- ICollectionWrapperFactory - Membuat koleksi (yang melampaui IEnumerable).
- IQueryFilterFactory - Menghasilkan kueri berdasarkan input yang akan meminta db.
- IIdentityHelper - Mengambil pengguna yang login.
- IFaultFactory - Buat FaultExceptions yang berbeda (saya menggunakan WCF).
Saya tidak benar-benar kecewa dengan bagaimana saya mendelegasikan tanggung jawab, tetapi saya mulai khawatir dengan keterbacaan.
Jadi, pertanyaan saya:
Apakah ada batasan berapa banyak suntikan yang harus dimiliki kelas? Dan jika demikian, bagaimana cara menghindarinya?
Apakah banyak suntikan membatasi keterbacaan, atau apakah benar-benar memperbaikinya?
sumber
Jawaban:
Terlalu banyak dependensi dapat mengindikasikan bahwa kelas itu sendiri melakukan terlalu banyak. Untuk menentukan apakah tidak melakukan terlalu banyak:
Lihatlah kelas itu sendiri. Apakah masuk akal untuk membaginya menjadi dua, tiga, empat? Apakah ini masuk akal secara keseluruhan?
Lihatlah jenis-jenis dependensi. Yang mana yang spesifik domain, dan mana yang “global”? Sebagai contoh, saya tidak akan mempertimbangkan
ILog
pada tingkat yang sama denganIQueryFilterFactory
: yang pertama akan tersedia di sebagian besar kelas bisnis jika mereka menggunakan logging. Di sisi lain, jika Anda menemukan banyak dependensi khusus domain, ini mungkin mengindikasikan bahwa kelas tersebut melakukan terlalu banyak.Lihatlah dependensi yang dapat diganti dengan nilai.
Ini dapat dengan mudah diganti dengan
DateTime.Now
diteruskan langsung ke metode yang perlu mengetahui waktu saat ini.Dengan melihat dependensi yang sebenarnya, saya tidak melihat apa pun yang mengindikasikan hal-hal buruk terjadi:
IDbContextFactory - membuat konteks untuk database.
Oke, kita mungkin berada di dalam lapisan bisnis tempat kelas berinteraksi dengan lapisan akses data. Terlihat baik.
IMapper - Memetakan dari entitas ke model domain.
Sulit mengatakan apa pun tanpa gambaran keseluruhan. Mungkin arsitekturnya salah dan pemetaannya harus dilakukan langsung oleh lapisan akses data, atau mungkin arsitekturnya baik-baik saja. Dalam semua kasus, masuk akal untuk memiliki ketergantungan ini di sini.
Pilihan lain adalah dengan membagi kelas menjadi dua: satu berurusan dengan pemetaan, yang lain berurusan dengan logika bisnis yang sebenarnya. Ini akan membuat layer de facto yang akan memisahkan BL lebih jauh dari DAL. Jika pemetaan itu rumit, itu bisa menjadi ide yang bagus. Namun, dalam banyak kasus, itu hanya akan menambah kompleksitas yang tidak berguna.
IClock - Abstracts DateTime. Sekarang dapat membantu dengan unit test.
Mungkin tidak terlalu berguna untuk memiliki antarmuka yang terpisah (dan kelas) hanya untuk mendapatkan waktu saat ini. Saya hanya akan meneruskan
DateTime.Now
ke metode yang memerlukan waktu saat ini.Kelas terpisah mungkin masuk akal jika ada beberapa informasi lain, seperti zona waktu, atau rentang tanggal, dll.
IPerformanceFactory - mengukur waktu eksekusi untuk metode tertentu.
Lihat poin selanjutnya.
ILog - Log4net untuk logging.
Fungsionalitas transendan seperti itu harus menjadi bagian dari kerangka kerja, dan pustaka yang sebenarnya harus dapat dipertukarkan dan dapat dikonfigurasi saat runtime (misalnya melalui app.config dalam .NET).
Sayangnya, ini bukan (belum) kasusnya, yang memungkinkan Anda memilih perpustakaan dan tetap menggunakannya, atau membuat lapisan abstraksi untuk dapat menukar perpustakaan nanti jika diperlukan. Jika niat Anda khusus untuk tidak bergantung pada pilihan perpustakaan, lakukan saja. Jika Anda cukup yakin bahwa Anda akan terus menggunakan perpustakaan selama bertahun-tahun, jangan tambahkan abstraksi.
Jika perpustakaan terlalu kompleks untuk digunakan, pola fasad masuk akal.
ICollectionWrapperFactory - Membuat koleksi (yang melampaui IEnumerable).
Saya akan berasumsi bahwa ini menciptakan struktur data yang sangat spesifik yang digunakan oleh logika domain. Itu terlihat seperti kelas utilitas. Sebagai gantinya, gunakan satu kelas per struktur data dengan konstruktor yang relevan. Jika logika inisialisasi sedikit rumit agar sesuai dengan konstruktor, gunakan metode pabrik statis. Jika logikanya lebih kompleks, gunakan pola pabrik atau pembangun.
IQueryFilterFactory - Menghasilkan kueri berdasarkan input yang akan meminta db.
Mengapa tidak ada di lapisan akses data? Mengapa ada
Filter
nama?IIdentityHelper - Mengambil pengguna yang login.
Saya tidak yakin mengapa ada
Helper
sufiks. Dalam semua kasus, sufiks lain tidak akan terlalu eksplisit (IIdentityManager
?)Bagaimanapun, masuk akal untuk memiliki ketergantungan ini di sini.
IFaultFactory - Buat FaultExceptions yang berbeda (saya menggunakan WCF).
Logikanya sangat rumit sehingga diperlukan untuk menggunakan pola pabrik? Mengapa Injeksi Ketergantungan digunakan untuk itu? Apakah Anda akan menukar pembuatan pengecualian antara kode produksi dan pengujian? Mengapa?
Saya akan mencoba untuk mengubahnya menjadi sederhana
throw new FaultException(...)
. Jika beberapa informasi global harus ditambahkan ke semua pengecualian sebelum menyebarkannya ke klien, WCF mungkin memiliki mekanisme di mana Anda menangkap pengecualian yang tidak tertangani dan dapat mengubahnya dan mengembalikannya ke klien.Mengukur kualitas dengan angka biasanya sama buruknya dengan dibayar oleh baris kode yang Anda tulis per bulan. Anda mungkin memiliki banyak dependensi dalam kelas yang dirancang dengan baik, karena Anda dapat memiliki kelas jelek menggunakan beberapa dependensi.
Banyak ketergantungan membuat logika lebih sulit untuk diikuti. Jika logikanya sulit untuk diikuti, kelas mungkin melakukan terlalu banyak dan harus dibagi.
sumber
Ini adalah contoh klasik dari DI yang memberi tahu Anda bahwa kelas Anda mungkin menjadi terlalu besar untuk menjadi satu kelas. Ini sering ditafsirkan sebagai "wow, DI membuat konstruktor saya lebih besar dari jupiter, teknik ini mengerikan", tetapi apa yang SEBENARNYA memberitahu Anda adalah bahwa "kelas Anda memiliki banyak dependensi". Mengetahui hal ini, kita juga bisa
Ada banyak cara untuk mengelola dependensi, dan tidak mungkin untuk mengatakan apa yang paling berhasil dalam kasus Anda tanpa mengetahui kode dan aplikasi Anda.
Untuk menjawab pertanyaan terakhir Anda:
Ya, batas atas adalah "terlalu banyak". Berapa banyak "terlalu banyak"? "Terlalu banyak" adalah ketika kohesi kelas menjadi "terlalu rendah". Semuanya tergantung. Biasanya jika reaksi Anda terhadap kelas adalah "wow, benda ini memiliki banyak ketergantungan", itu terlalu banyak.
Saya pikir pertanyaan ini menyesatkan. Jawabannya bisa ya atau tidak. Tapi itu bukan bagian yang paling menarik. Inti dari ketergantungan injeksi adalah untuk membuatnya terlihat. Ini tentang membuat api yang tidak bohong. Ini tentang mencegah negara global. Ini tentang membuat kode dapat diuji. Ini tentang mengurangi kopling.
Kelas yang dirancang dengan baik dengan metode yang dirancang dan dinamai lebih mudah dibaca daripada yang dirancang dengan buruk. DI tidak benar-benar meningkatkan atau melukai keterbacaan semata, itu hanya membuat pilihan desain Anda menonjol, dan jika mereka buruk itu akan menyengat mata Anda. Ini tidak berarti DI membuat kode Anda kurang mudah dibaca, itu hanya menunjukkan kepada Anda bahwa kode Anda sudah berantakan, Anda baru saja menyembunyikannya.
sumber
Menurut Steve McConnel, penulis "Kode Lengkap" yang ada di mana-mana, aturan praktisnya adalah lebih dari 7 adalah bau kode yang mengganggu pemeliharaan. Secara pribadi saya pikir angkanya lebih rendah dalam kebanyakan kasus, tetapi melakukan DI dengan benar akan menghasilkan banyak ketergantungan untuk disuntikkan ketika Anda sangat dekat dengan Komposisi Root. Ini normal dan diharapkan. Itu salah satu alasan bahwa wadah IoC adalah hal yang berguna dan sebanding dengan kompleksitas yang mereka tambahkan ke proyek.
Jadi, jika Anda sangat dekat dengan titik masuk program Anda, ini normal dan dapat diterima. Jika Anda lebih dalam ke logika program, kemungkinan bau yang harus diatasi.
sumber