Basit bir örnek verelim - belki bir günlük kaydı enjekte ediyorsunuz.
Sınıf enjekte etme
class Worker: IWorker
{
ILogger _logger;
Worker(ILogger logger)
{
_logger = logger;
}
void SomeMethod()
{
_logger.Debug("This is a debug log statement.");
}
}
Bence neler olduğu çok açık. Dahası, bir IoC kabı kullanıyorsanız, açıkça bir şey enjekte etmeniz bile gerekmez, sadece kompozisyon kökünüze eklersiniz:
container.RegisterType<ILogger, ConcreteLogger>();
container.RegisterType<IWorker, Worker>();
....
var worker = container.Resolve<IWorker>();
Hata ayıklama sırasında Worker
, bir geliştiricinin hangi beton sınıfının kullanıldığını belirlemek için kompozisyon köküne danışması yeterlidir.
Bir geliştiricinin daha karmaşık mantığa ihtiyacı varsa, çalışmak için tüm arayüze sahiptir:
void SomeMethod()
{
if (_logger.IsDebugEnabled) {
_logger.Debug("This is a debug log statement.");
}
}
Bir yöntem enjekte etme
class Worker
{
Action<string> _methodThatLogs;
Worker(Action<string> methodThatLogs)
{
_methodThatLogs = methodThatLogs;
}
void SomeMethod()
{
_methodThatLogs("This is a logging statement");
}
}
İlk olarak, yapıcı parametresinin artık daha uzun bir adı olduğuna dikkat edin methodThatLogs
. Bu gereklidir, çünkü bir Action<string>
kişinin ne yapması gerektiğini söyleyemezsiniz . Arayüz ile tamamen açıktı, ancak burada parametre adlandırmaya dayanmak zorundayız. Bu, doğal olarak daha az güvenilir ve bir yapı sırasında uygulanması zor görünüyor.
Şimdi, bu yöntemi nasıl enjekte ederiz? IoC kapsayıcısı sizin için yapmaz. Böylece, somutlaştırdığınızda açıkça enjekte edersiniz Worker
. Bu birkaç problem yaratır:
- Bir örnek oluşturmak daha fazla iştir
Worker
- Hata ayıklamaya çalışan geliştiriciler
Worker
, somut örneğin ne denildiğini anlamanın daha zor olduğunu göreceklerdir. Sadece kompozisyon köküne danışamazlar; kod üzerinden izlemeleri gerekecek.
Daha karmaşık mantığa ihtiyacımız olursa ne olur? Tekniğiniz sadece bir yöntem ortaya koyar. Şimdi sanırım karmaşık şeyleri lambdaya fırlatabilirsin:
var worker = new Worker((s) => { if (log.IsDebugEnabled) log.Debug(s) } );
ancak birim testlerinizi yazarken, bu lambda ifadesini nasıl test edersiniz? Anonimdir, bu nedenle birim test çerçeveniz doğrudan başlatamaz. Belki de bunu yapmanın akıllıca bir yolunu bulabilirsiniz, ancak muhtemelen bir arayüz kullanmaktan daha büyük bir PITA olacaktır.
Farklılıkların özeti:
- Sadece bir yöntemin enjekte edilmesi, amacın çıkarımını zorlaştırırken, bir arayüz amacı açıkça iletir.
- Sadece bir yöntemin enjekte edilmesi, enjeksiyonu alan sınıfa daha az işlevsellik kazandırır. Bugün ihtiyacın olmasa bile, yarın ihtiyacın olabilir.
- Bir IoC kapsayıcısını kullanarak yalnızca bir yöntemi otomatik olarak enjekte edemezsiniz.
- Belirli bir durumda hangi kök sınıfının iş başında olduğunu kompozisyon kökünden söyleyemezsiniz.
- Lambda ifadesinin kendisini birim olarak test etmek bir sorundur.
Yukarıdakilerin hepsinde sorun yoksa, sadece yöntemi enjekte etmek sorun yaratmaz. Aksi takdirde geleneğe bağlı kalmanızı ve bir arayüz enjekte etmenizi öneririm.