6

Tüketicilere genel bir koleksiyon türü sağlayacak bir kitaplık yazıyorum.Bir kütüphane projesinde yapıcı argümanları için varsayılan değerler

Bu kütüphane bağımlılığı enjeksiyon dostu türlerini yapmak istiyorum. Bu, her sınıfın başlatılan nesnenin her bir bağımlılığını belirtmenin mümkün olduğu bir kurucuya sahip olması gerektiği anlamına gelir. Ayrıca kütüphanenin konfigürasyon prensibine göre sözleşmeye bağlı kalmasını istiyorum. Bu, bir tüketici varsayılan davranışı istiyorsa, parametresiz bir kurucu kullanabilir ve nesne bir şekilde bağımlılıkları kendi başına kuracaktır demektir. Örnekte

(C#):

public class Samurai { 

    private readonly IWeapon _weapon; 

    // consumers will use this constructor most of the time 
    public Samurai() { 
     _weapon = ??? // get an instance of the default weapon somehow 
    } 

    // consumers will use this constructor if they want to explicitly 
    // configure dependencies for this instance 
    public Samurai(IWeapon weapon) { 
     _weapon = weapon; 
    } 
} 

Benim ilk çözüm hizmeti bulucu desen kullanmak olacaktır.

kod şu şekilde görünecektir:

... 
public Samurai() { 
    _weapon = ServiceLocator.Instance.Get<IWeapon>(); 
} 
... 

Gerçi bu bir sorun var. Servis bulucu bir anti-pattern (link) olarak işaretlendi ve bu argümanlarla tamamen katılıyorum. Öte yandan Martin Fowler, servis belirleyici modelinin kullanımını tam olarak bu durumda (kütüphane projeleri) kullanmaktadır (link). Dikkatli olmak istiyorum ve servis belirleyicisinin gerçekten kötü bir fikir olduğunu gösterdikten sonra kütüphaneyi yeniden yazmak için gerekli gerekliliği ortadan kaldırmak istiyorum. Sonuç olarak - bu senaryoda servis belirleyicisinin iyi olduğunu mu düşünüyorsunuz? Problemimi tamamen farklı bir şekilde çözmeli miyim?

public interface IWeaponProvider 
{ 
    IWeapon GetWeapon(); 
} 

public class Samurai 
{ 
    private readonly IWeapon _weapon; 

    public Samurai(IWeaponProvider provider) 
    { 
     _weapon = provider.GetWeapon(); 
    } 
} 

Şimdi: Herhangi bir düşünce alternatif yoktur

cevap

4

Eğer böyle yöntemleri vardır adanmış Defaults sınıfın aracılığıyla varsayılan örneklerini sağlayacak bir DI konteyner kullanmayan kullanıcılar için hayatı kolaylaştırmak isterseniz:

public virtual Samurai CreateDefaultSamurai() 
{ 
    return new Samurai(CreateDefaultWeapon()); 
} 

public virtual IWeapon CreateDefaultWeapon() 
{ 
    return new Shuriken(); 
} 

Bu şekilde size sınıfları kendilerini varsayılan kurucularla kirletmeye gerek yoktur ve kullanıcılarınız bu varsayılan kurucuları istemeden kullanmama riskiyle karşı karşıya değildir.

+2

DI sınıfı bir dostu tutmak önemlidir. +1 –

1

, yani belirli bir sağlayıcı enjekte edilir, sizin için arama yapmak üzere sizinle sınıfa sizin durumunuzda bir WeaponProvider diyelim ... açığız Eğer bir silah için yerel varsayılan sağlayıcı sağlayabilir:

public class DefaultWeaponProvider : IWeaponProvider 
{ 
    public IWeapon GetWeapon() 
    { 
     return new Sword(); 
    } 
} 

Ve bu yerel varsayılan (bir "piç enjeksiyon" değil yani, farklı bir derlemesinden birine aksine) olduğundan, size olarak kullanabilirsiniz Samurai sınıfınızın bir kısmı da:

public class Samurai 
{ 
    private readonly IWeapon _weapon; 

    public Samurai() : this(new DefaultWeaponProvider()) 
    { 
    } 

    public Samurai(IWeaponProvider provider) 
    { 
     _weapon = provider.GetWeapon(); 
    } 
} 
+0

Bu gerçekten çok şık bir alternatif gibi görünüyor.Sadece bir soru daha sormak istiyorum. – Tomas

+0

Şimdi IWeaponProvider'ın Samurai sınıfının yapıcısında DefaultWeaponProvider'a bağlanmasını kodlamalıyım. Bu bağlamanın bazı kompozisyon modüllerine nasıl yeniden yapılandırılabileceğini görüyor musunuz? IOC kapsayıcılarında olduğu gibi, "Bind (). ();" gibi bir şeyi tanımladığınız tek bir yapılandırma dosyanız vardır. (- Ninject'den örnek). Bunu yapmanın bir yolu olmasa bile, cevabınızı kabul edeceğim. Sadece açıklığa kavuşturmak istedim ... Teşekkürler – Tomas

+0

sadece açıklığa kavuşturmak için: "kongre üzerinden yapılandırma" ile demek istiyorsun * hala * bu varsayılan değerlerin ne olacağını yapılandırmak ister misin? Ama evet, 'DefaultWeaponProvider'ınızı gerçekten de' IWeapon 'işlevini gerçekten bir dosyaya, yani bir config dosyasına veya bir IoC konteynerinin dahili kullanımına dayanacak şekilde uygulayabilirsiniz - ama' Samurai 'sınıfınız bu bağımlılıklardan hiçbirine sahip olmayacaktır – BrokenGlass

1

C# projemde aşağıdaki yaklaşımı kullandım. Amaç, bir "normal kullanım durumu" için kodun uygulanmasını sakınmamakla birlikte (yani, yürütme akışı boyunca kademeli olarak büyük miktarda yeni() sahip olmak) bağımlılık enjeksiyonu (birim/mock testi için) elde etmekti.

public sealed class QueueProcessor : IQueueProcessor 
{ 
    private IVbfInventory vbfInventory; 
    private IVbfRetryList vbfRetryList; 

    public QueueProcessor(IVbfInventory vbfInventory = null, IVbfRetryList vbfRetryList = null) 
    { 
     this.vbfInventory = vbfInventory ?? new VbfInventory(); 
     this.vbfRetryList = vbfRetryList ?? new VbfRetryList(); 
    } 
} 

Bu DI verir ama aynı zamanda herhangi bir tüketici olmalı "varsayılan örnek akış" olduğunu dert zorunda değildir anlamına gelir.

İlgili konular