Berapa banyak suntikan yang dapat diterima dalam satu kelas saat menggunakan injeksi ketergantungan

9

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?

smoksnes
sumber
2
Mengukur kualitas dengan angka biasanya sama buruknya dengan dibayar oleh baris kode yang Anda tulis per bulan. Namun, Anda menyertakan contoh dependensi yang sebenarnya, yang merupakan ide bagus. Jika saya adalah Anda, saya akan merumuskan kembali pertanyaan Anda untuk menghapus konsep penghitungan dan batasan ketat, dan untuk fokus alih-alih kualitas semata. Saat merumuskannya kembali, berhati-hatilah untuk tidak membuat pertanyaan Anda terlalu spesifik.
Arseni Mourzenko
3
Berapa banyak argumen konstruktor yang dapat diterima? IoC tidak mengubahnya .
Telastyn
Mengapa orang selalu menginginkan batas absolut?
Marjan Venema
1
@ Telastyn: belum tentu. Apa perubahan IoC adalah bahwa alih-alih mengandalkan kelas statis / lajang / variabel global, semua dependensi lebih terpusat dan lebih "terlihat".
Arseni Mourzenko
@MarjanVenema: karena itu membuat hidup jauh lebih mudah. Jika Anda tahu persis LOC maksimum per metode atau jumlah metode maksimum dalam suatu kelas atau jumlah variabel maksimum pada suatu metode, dan ini adalah satu-satunya hal yang penting, menjadi mudah untuk memenuhi syarat kode tersebut baik atau buruk, seperti serta "memperbaiki" kode yang buruk. Sangat disayangkan bahwa kehidupan nyata jauh lebih kompleks dari itu dan banyak metrik yang sebagian besar tidak relevan.
Arseni Mourzenko

Jawaban:

11

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 ILogpada tingkat yang sama dengan IQueryFilterFactory: 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.

    IClock - Abstracts DateTime. Sekarang dapat membantu dengan unit test.

    Ini dapat dengan mudah diganti dengan DateTime.Nowditeruskan 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.Nowke 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 Filternama?

  • IIdentityHelper - Mengambil pengguna yang login.

    Saya tidak yakin mengapa ada Helpersufiks. 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.

Apakah ada batasan berapa banyak suntikan yang harus dimiliki kelas? Dan jika demikian, bagaimana cara menghindarinya?

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.

Apakah banyak suntikan membatasi keterbacaan, atau apakah benar-benar memperbaikinya?

Banyak ketergantungan membuat logika lebih sulit untuk diikuti. Jika logikanya sulit untuk diikuti, kelas mungkin melakukan terlalu banyak dan harus dibagi.

Arseni Mourzenko
sumber
Terima kasih atas komentar dan waktu Anda. Sebagian besar tentang FaultFactory yang akan dipindahkan ke logika WCF sebagai gantinya. Saya akan menyimpan IClock karena ini merupakan penyelamat saat TDD aplikasi. Ada beberapa kali ketika Anda ingin memastikan bahwa nilai tertentu ditetapkan dengan waktu tertentu. Maka DateTime. Sekarang tidak akan selalu cukup karena itu tidak bisa dipermainkan.
smoksnes
7

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

  • Sapu masalah di bawah permadani dengan memulai dependensi baru sebagai gantinya
  • Pertimbangkan kembali desain kami. Mungkin beberapa dependensi selalu bersatu dan harus disembunyikan di balik abstraksi lain. Mungkin kelas Anda harus dibagi menjadi 2. Mungkin harus disusun oleh beberapa kelas yang masing-masing memerlukan subset kecil dari dependensi.

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:

  • Apakah ada batasan atas berapa banyak dependensi yang harus dimiliki suatu kelas?

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.

  • Apakah ketergantungan suntikan meningkatkan atau merusak keterbacaan?

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.

sara
sumber
1
Umpan balik yang bagus. Terima kasih. Ya, batas atas adalah "terlalu banyak". - Fantastis, dan sangat benar.
smoksnes
3

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.

Bebek karet
sumber