Saya sudah banyak kelas inti yang memerlukan ISessionContext dari database, ILogManager untuk log dan IService digunakan untuk berkomunikasi dengan layanan lain. Saya ingin menggunakan injeksi dependensi untuk kelas ini yang digunakan oleh semua kelas inti.
Saya sudah dua kemungkinan implementasi. Kelas inti yang menerima IAmbientContext dengan semua tiga kelas atau menyuntikkan untuk semua kelas tiga kelas.
public interface ISessionContext
{
...
}
public class MySessionContext: ISessionContext
{
...
}
public interface ILogManager
{
}
public class MyLogManager: ILogManager
{
...
}
public interface IService
{
...
}
public class MyService: IService
{
...
}
Solusi pertama:
public class AmbientContext
{
private ISessionContext sessionContext;
private ILogManager logManager;
private IService service;
public AmbientContext(ISessionContext sessionContext, ILogManager logManager, IService service)
{
this.sessionContext = sessionContext;
this.logManager = logManager;
this.service = service;
}
}
public class MyCoreClass(AmbientContext ambientContext)
{
...
}
solusi kedua (tanpa ambientcontext)
public MyCoreClass(ISessionContext sessionContext, ILogManager logManager, IService service)
{
...
}
Apakah solusi terbaik dalam hal ini?
c#
dependency-injection
pengguna3401335
sumber
sumber
IService
digunakan untuk berkomunikasi dengan layanan lain?" JikaIService
mewakili ketergantungan samar pada layanan lain maka itu terdengar seperti pencari lokasi dan seharusnya tidak ada. Kelas Anda harus bergantung pada antarmuka yang secara eksplisit menggambarkan apa yang akan dilakukan konsumen dengan mereka. Tidak ada kelas yang membutuhkan layanan untuk menyediakan akses ke layanan. Kelas membutuhkan ketergantungan yang melakukan sesuatu yang spesifik yang dibutuhkan kelas.Jawaban:
"Terbaik" terlalu subyektif di sini. Seperti yang biasa terjadi pada keputusan semacam itu, ini merupakan pertukaran antara dua cara yang sama validnya untuk mencapai sesuatu.
Jika Anda membuat
AmbientContext
dan menyuntikkan itu ke banyak kelas, Anda berpotensi memberikan informasi lebih lanjut untuk masing-masing dari mereka perlu (misalnya kelasFoo
hanya dapat menggunakanISessionContext
, tapi sedang diberitahu tentangILogManager
danISession
juga).Jika Anda mengirimkan masing-masing melalui parameter, maka Anda memberi tahu setiap kelas hanya tentang hal-hal yang perlu diketahui. Tetapi, jumlah parameter dapat dengan cepat bertambah dan Anda mungkin mendapati Anda memiliki terlalu banyak konstruktor dan metode dengan banyak parameter, yang diulang-ulang, yang dapat disederhanakan melalui kelas konteks.
Jadi ini adalah kasus menyeimbangkan keduanya dan memilih yang sesuai untuk keadaan Anda. Jika Anda hanya memiliki satu kelas dan tiga parameter, saya pribadi tidak akan peduli
AmbientContext
. Bagi saya, titik kritis kemungkinan adalah empat parameter. Tapi itu pendapat murni. Titik kritis Anda mungkin akan berbeda dengan saya, jadi ikuti dengan apa yang terasa benar bagi Anda.sumber
Terminologi dari pertanyaan tidak benar-benar cocok dengan kode contoh. Ini
Ambient Context
adalah pola yang digunakan untuk mengambil ketergantungan dari kelas mana saja di modul apa pun semudah mungkin, tanpa mencemari setiap kelas untuk menerima antarmuka ketergantungan, tetapi tetap mempertahankan gagasan inversi kontrol. Ketergantungan seperti itu biasanya didedikasikan untuk logging, keamanan, manajemen sesi, transaksi, caching, audit sehingga untuk masalah lintas sektoral dalam aplikasi itu. Ini entah bagaimana menjengkelkan untuk menambahkanILogging
,ISecurity
,ITimeProvider
untuk konstruktor dan sebagian besar waktu tidak semua kelas perlu semua pada saat yang sama, jadi saya mengerti kebutuhan Anda.Bagaimana jika masa pakai
ISession
instance berbeda dengan yangILogger
ada? Mungkin instance ISession harus dibuat pada setiap permintaan dan ILogger sekali. Jadi memiliki semua dependensi ini diatur oleh satu objek yang bukan wadah itu sendiri tidak terlihat pilihan yang tepat karena semua masalah ini dengan manajemen seumur hidup dan lokalisasi dan lainnya yang dijelaskan dalam utas ini.Pertanyaan
IAmbientContext
di atas tidak menyelesaikan masalah tidak mencemari setiap konstruktor. Anda masih harus menggunakannya dalam tanda tangan konstruktor, tentu saja, hanya sekali saja kali ini.Jadi cara termudah BUKAN menggunakan injeksi konstruktor atau mekanisme injeksi lainnya untuk menangani dependensi lintas bidang, tetapi menggunakan panggilan statis . Kami sebenarnya melihat pola ini cukup sering, diimplementasikan oleh kerangka itu sendiri. Periksa Thread.CurrentPrincipal yang merupakan properti statis yang mengembalikan implementasi
IPrincipal
antarmuka. Ini juga dapat diatur sehingga Anda dapat mengubah implementasinya jika Anda suka, sehingga Anda tidak dapat menggunakannya.MyCore
terlihat seperti sekarangPola ini dan kemungkinan implementasi telah dijelaskan secara rinci oleh Mark Seemann dalam artikel ini . Mungkin ada implementasi yang bergantung pada wadah IoC sendiri yang Anda gunakan.
Anda ingin menghindari
AmbientContext.Current.Logger
,AmbientContext.Current.Session
untuk alasan yang sama seperti yang dijelaskan di atas.Tetapi Anda memiliki opsi lain untuk menyelesaikan masalah ini: gunakan dekorator, intersepsi dinamis jika wadah Anda memiliki kemampuan ini atau AOP. Ambient Context harus menjadi pilihan terakhir karena kliennya menyembunyikan ketergantungan mereka melaluinya. Saya masih akan menggunakan Ambient Context jika antarmuka itu benar-benar meniru dorongan saya untuk menggunakan dependensi statis seperti
DateTime.Now
atauConfigurationManager.AppSettings
dan kebutuhan ini meningkat cukup sering. Tetapi pada akhirnya injeksi konstruktor mungkin bukan ide yang buruk untuk mendapatkan dependensi di mana-mana.sumber
Saya akan menghindari
AmbientContext
.Pertama, jika kelas tergantung,
AmbientContext
Anda tidak benar-benar tahu apa fungsinya. Anda harus melihat penggunaan ketergantungan itu untuk mencari tahu mana dependensi bersarang yang digunakannya. Anda juga tidak dapat melihat jumlah dependensi dan mengetahui apakah kelas Anda melakukan terlalu banyak karena salah satu dependensi itu sebenarnya mewakili beberapa dependensi bersarang.Kedua, jika Anda menggunakannya untuk menghindari beberapa dependensi konstruktor, maka pendekatan ini akan mendorong pengembang lain (termasuk Anda) untuk menambahkan anggota baru ke kelas konteks sekitar. Kemudian masalah pertama diperparah.
Ketiga, mengejek ketergantungan pada
AmbientContext
lebih sulit karena Anda harus mencari tahu dalam setiap kasus apakah mengolok-olok semua anggotanya atau hanya yang Anda butuhkan, dan kemudian membuat tiruan yang mengembalikan mengejek itu (atau menguji ganda.) Itu membuat unit Anda lebih sulit untuk menulis, membaca, dan memelihara.Keempat, tidak memiliki kohesi dan melanggar Prinsip Tanggung Jawab Tunggal. Itu sebabnya ia memiliki nama seperti "AmbientContext," karena ia melakukan banyak hal yang tidak berhubungan dan tidak ada cara untuk memberi nama sesuai dengan fungsinya.
Dan itu mungkin melanggar Prinsip Segregasi Antarmuka dengan memperkenalkan anggota antarmuka ke dalam kelas yang tidak membutuhkannya.
sumber
Yang kedua (tanpa pembungkus antarmuka)
Kecuali jika ada beberapa interaksi antara berbagai layanan yang perlu dienkapsulasi dalam kelas perantara, itu hanya mempersulit kode Anda dan membatasi fleksibilitas ketika Anda memperkenalkan 'antarmuka antarmuka'
sumber