ortam bağlamı ve yapıcı enjeksiyonu


9

ISessionContext veritabanı, günlük için ILogManager ve başka bir hizmet ile iletişim için kullanılan IService gerektiren birçok çekirdek sınıf var. Tüm çekirdek sınıflar tarafından kullanılan bu sınıf için bağımlılık enjeksiyonu kullanmak istiyorum.

İki olası uygulamam var. IAmbientContext'i üç sınıfın tümü ile kabul eden veya tüm sınıf için üç sınıfın enjekte ettiği çekirdek sınıf.

public interface ISessionContext 
{
    ...
}

public class MySessionContext: ISessionContext 
{
    ...
}

public interface ILogManager 
{

}

public class MyLogManager: ILogManager 
{
    ...
}

public interface IService 
{
    ...
}

public class MyService: IService
{
    ...
}

İlk çözüm:

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)
{
    ...
}

ikinci çözüm (ortam içeriği olmadan)

public MyCoreClass(ISessionContext sessionContext, ILogManager logManager, IService service)
{
    ...
}

Bu durumda en iyi çözüm hangisidir?


" IServiceBaşka bir hizmetle iletişim kurmak için kullanılır " nedir ? IServiceDiğer hizmetlere belirsiz bir bağımlılığı temsil ediyorsa , bir servis bulucu gibi görünür ve mevcut olmamalıdır. Sınıfınız, tüketicilerinin onlarla ne yapacağını açıkça tanımlayan arabirimlere bağlı olmalıdır. Hiçbir sınıfın bir hizmete erişim sağlamak için bir hizmete ihtiyacı yoktur. Bir sınıfın, sınıfın ihtiyaç duyduğu belirli bir şeyi yapan bir bağımlılığa ihtiyacı vardır.
Scott Hannen

Yanıtlar:


4

"En iyi" burada çok öznel. Bu tür kararlarda olduğu gibi, bir şeyi başarmanın eşit derecede geçerli iki yolu arasındaki bir ödünleşmedir.

Bunu oluşturur AmbientContextve birçok sınıfa enjekte ederseniz, potansiyel olarak her birine ihtiyaç duyduklarından daha fazla bilgi sağlıyorsunuz demektir (örneğin, sınıf Foosadece kullanabilir ISessionContext, ancak hakkında ILogManagerve ISessionayrıca söylenir ).

Her birini bir parametre aracılığıyla iletirseniz, her sınıfa yalnızca bilmesi gereken şeyler hakkında bilgi verirsiniz. Ancak, parametre sayısı hızlı bir şekilde büyüyebilir ve bir bağlam sınıfı aracılığıyla basitleştirilebilen birçok, çok tekrarlanan parametreye sahip çok fazla yapıcı ve yönteminiz olduğunu görebilirsiniz.

Yani bu ikisini dengelemek ve koşullarınız için uygun olanı seçmek durumunda. Sadece bir sınıf ve üç parametreniz varsa, şahsen rahatsız olmazdım AmbientContext. Benim için, devrilme noktası muhtemelen dört parametre olacaktır. Ama bu saf bir görüş. Devrilme noktanız muhtemelen benimkinden farklı olacaktır, bu yüzden size doğru gelenle devam edin.


4

Sorudaki terminoloji, örnek kodla gerçekten eşleşmiyor. Bu Ambient Context, her sınıfı bağımlılığın arayüzünü kabul etmek için kirletmeden, ancak yine de kontrolün ters çevrilmesi fikrini koruyarak, herhangi bir modüldeki herhangi bir sınıftan bir bağımlılığı olabildiğince kolay tutmak için kullanılan bir modeldir. Bu tür bağımlılıklar genellikle günlük tutma, güvenlik, oturum yönetimi, işlemler, önbellekleme, denetime adanmıştır, bu nedenle söz konusu uygulamadaki herhangi bir kesişen kaygı için. Bir eklemek nasılsa sinir bozucu ILogging, ISecurity, ITimeProviderçoğu zaman değil bütün sınıflar hepsi aynı anda ihtiyaç kurucular ve, senin ihtiyacını anlamaları için.

ISessionÖrneğin yaşam süresi birinden farklıysa ne olur ILogger? Belki ISession örneği her istekte ve ILogger'da bir kez oluşturulmalıdır. Bu nedenle, tüm bu bağımlılıkların konteynerin kendisi olmayan bir nesne tarafından yönetilmesi, ömür boyu yönetimi ve yerelleştirme ve bu iş parçacığında açıklanan diğer tüm sorunlar nedeniyle doğru seçim gibi görünmemektedir.

Söz IAmbientContextkonusu soru, her yapıcıyı kirletmeme sorununu çözmemektedir. Hala yapıcı imzasında kullanmak zorundasınız, elbette, bu sefer sadece bir kez.

Bu nedenle en kolay yol, çapraz kesim bağımlılıklarıyla uğraşmak için yapıcı enjeksiyonunu veya başka bir enjeksiyon mekanizmasını kullanmak değil, statik bir çağrı kullanmaktır . Aslında bu modeli çerçevenin kendisi tarafından uygulanan oldukça sık görüyoruz. Arabirim uygulamasını döndüren statik bir özellik olan Thread.CurrentPrincipal öğesini denetleyin IPrincipal. Ayrıca ayarlanabilir, böylece isterseniz uygulamayı değiştirebilirsiniz, böylece ona bağlı değilsiniz.

MyCore şimdi şuna benziyor

public class MyCoreClass
{
    public void BusinessFeature(string data)
    {
        LoggerContext.Current.Log(data);

        _repository.SaveProcessedData();

        SessionContext.Current.SetData(data);
        ...etc
    }
}

Bu model ve olası uygulamalar bu makalede Mark Seemann tarafından ayrıntılı olarak açıklanmıştır . Kullandığınız IoC konteynerinin kendisine dayanan uygulamalar olabilir.

Sen kaçınmak istediğiniz AmbientContext.Current.Logger, AmbientContext.Current.Sessionyukarıda açıklandığı gibi aynı nedenlerle.

Ancak bu sorunu çözmek için başka seçenekleriniz de var: dekoratörler kullanın, kabınız bu özelliğe veya AOP'ye sahipse dinamik müdahale. Ortam Bağlamı, müşterilerinin bağımlılıklarını onun aracılığıyla gizlemesi nedeniyle son çare olmalıdır. Arayüz gerçekten taklit benim dürtü statik gibi bağımlılık kullanmak eğer hala Ortam Bağlam kullanmak DateTime.Nowveya ConfigurationManager.AppSettingsve bu ihtiyaç oldukça sık yükseltir. Ancak sonunda yapıcı enjeksiyonu bu yaygın bağımlılıkları elde etmek için o kadar da kötü bir fikir olmayabilir.


3

Ben kaçınırdım AmbientContext.

İlk olarak, derse bağlıysa, AmbientContextaslında ne yaptığını bilmiyorsunuzdur. İç içe bağımlılıklarından hangisini kullandığını anlamak için bu bağımlılığı kullanmasına bakmalısınız. Ayrıca bağımlılık sayısına bakamaz ve sınıfınızın çok fazla iş yapıp yapmadığını söyleyemezsiniz çünkü bu bağımlılıklardan biri aslında birkaç iç içe bağımlılığı temsil edebilir.

İkincisi, birden fazla kurucu bağımlılığını önlemek için kullanıyorsanız, bu yaklaşım diğer geliştiricileri (kendiniz dahil) bu ortam bağlam sınıfına yeni üyeler eklemeye teşvik edecektir. Daha sonra ilk sorun birleştirilir.

Üçüncüsü, bağımlılığı alay etmek AmbientContextdaha zordur, çünkü her durumda tüm üyelerini mi yoksa sadece ihtiyacınız olanları mı alay edip etmediğini anlamanız ve daha sonra bu alayları döndüren bir alay kurmanız gerekir. üniteniz yazma, okuma ve bakım işlemlerini daha zor test eder.

Dördüncüsü, uyumdan yoksundur ve Tek Sorumluluk İlkesini ihlal eder. Bu yüzden "AmbientContext" gibi bir adı var, çünkü birçok alakasız şey yapıyor ve ne yaptığına göre adlandırmanın bir yolu yok.

Ve muhtemelen arayüz üyelerini onlara ihtiyaç duymayan sınıflara sokarak Arayüz Ayırma İlkesini ihlal ediyor.


2

İkincisi (arayüz sarmalayıcısı olmadan)

Bir ara sınıfta kapsüllenmesi gereken çeşitli hizmetler arasında bir etkileşim olmadığı sürece, yalnızca kodunuzu karmaşıklaştırır ve 'arabirimler arabirimini' tanıtırken esnekliği sınırlar

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.