Mari kita ambil contoh sederhana - mungkin Anda menyuntikkan alat logging.
Menyuntikkan kelas
class Worker: IWorker
{
ILogger _logger;
Worker(ILogger logger)
{
_logger = logger;
}
void SomeMethod()
{
_logger.Debug("This is a debug log statement.");
}
}
Saya pikir itu cukup jelas apa yang sedang terjadi. Terlebih lagi, jika Anda menggunakan wadah IoC, Anda bahkan tidak perlu menyuntikkan sesuatu secara eksplisit, Anda cukup menambahkan ke root komposisi Anda:
container.RegisterType<ILogger, ConcreteLogger>();
container.RegisterType<IWorker, Worker>();
....
var worker = container.Resolve<IWorker>();
Saat debugging Worker
, pengembang hanya perlu berkonsultasi dengan root komposisi untuk menentukan kelas konkret yang digunakan.
Jika pengembang membutuhkan logika yang lebih rumit, ia memiliki seluruh antarmuka untuk bekerja dengan:
void SomeMethod()
{
if (_logger.IsDebugEnabled) {
_logger.Debug("This is a debug log statement.");
}
}
Menyuntikkan suatu metode
class Worker
{
Action<string> _methodThatLogs;
Worker(Action<string> methodThatLogs)
{
_methodThatLogs = methodThatLogs;
}
void SomeMethod()
{
_methodThatLogs("This is a logging statement");
}
}
Pertama, perhatikan bahwa parameter konstruktor memiliki nama yang lebih panjang sekarang methodThatLogs
,. Ini perlu karena Anda tidak tahu apa yang Action<string>
seharusnya dilakukan. Dengan antarmuka, itu benar-benar jelas, tapi di sini kita harus mengandalkan penamaan parameter. Ini tampaknya secara inheren kurang dapat diandalkan dan lebih sulit untuk ditegakkan selama membangun.
Sekarang, bagaimana kita menyuntikkan metode ini? Nah, wadah IoC tidak akan melakukannya untuk Anda. Jadi, Anda menyuntikkannya secara eksplisit saat Anda membuat instantiate Worker
. Ini menimbulkan beberapa masalah:
- Lebih banyak pekerjaan untuk instantiate a
Worker
- Pengembang yang mencoba men-debug
Worker
akan merasa lebih sulit untuk mencari tahu apa contoh konkret dipanggil. Mereka tidak bisa hanya berkonsultasi dengan komposisi root; mereka harus menelusuri kode.
Bagaimana kalau kita membutuhkan logika yang lebih rumit? Teknik Anda hanya memaparkan satu metode. Sekarang saya kira Anda bisa memanggang hal-hal rumit ke dalam lambda:
var worker = new Worker((s) => { if (log.IsDebugEnabled) log.Debug(s) } );
tetapi ketika Anda menulis unit test Anda, bagaimana Anda menguji ekspresi lambda itu? Ini anonim, jadi kerangka kerja unit test Anda tidak dapat membuat instantiate secara langsung. Mungkin Anda bisa mencari cara cerdas untuk melakukannya, tetapi itu mungkin akan menjadi PITA lebih besar daripada menggunakan antarmuka.
Ringkasan perbedaan:
- Menyuntikkan hanya metode membuat lebih sulit untuk menyimpulkan tujuan, sedangkan antarmuka dengan jelas mengomunikasikan tujuan.
- Menyuntikkan hanya metode memperlihatkan fungsi yang kurang ke kelas yang menerima injeksi. Bahkan jika Anda tidak membutuhkannya hari ini, Anda mungkin membutuhkannya besok.
- Anda tidak dapat secara otomatis menyuntikkan hanya metode menggunakan wadah IoC.
- Anda tidak dapat mengetahui dari akar komposisi kelas beton mana yang bekerja dalam contoh tertentu.
- Merupakan masalah untuk menguji unit ekspresi lambda itu sendiri.
Jika Anda baik-baik saja dengan semua hal di atas, maka tidak apa-apa untuk menyuntikkan metode saja. Kalau tidak, saya sarankan Anda tetap dengan tradisi dan menyuntikkan antarmuka.