Kod tabanını kademeli olarak bağımlılık enjeksiyon kabına taşıyın


9

Ben "anti-desen" singletons, statik yöntemler ile yardımcı sınıfları ve newanahtar kelimeleri kullanarak kendi bağımlılıkları oluşturma sınıfları ile büyük bir kod temeli var . Bir kodun test edilmesini çok zorlaştırır.

Kod yavaş yavaş bağımlılık enjeksiyon kabına taşımak istiyorum (benim durumumda Guice, çünkü bir GWTproje olduğu için ). Bağımlılık enjeksiyonu anlayışımdan, hepsi ya da hiç. Tüm sınıflar Bahar / Guice tarafından yönetilir veya hiçbiri tarafından yönetilmez. Kod tabanı büyük olduğu için kodu gece boyunca değiştiremiyorum. Bu yüzden yavaş yavaş yapmanın bir yoluna ihtiyacım var.

Sorun şu ki, diğer sınıflara enjekte edilmesi gereken bir sınıfla başladığımda @Inject, bu sınıflarda basit kullanamıyorum çünkü bu sınıflar henüz kapsayıcı tarafından yönetilmiyor. Böylece bu, hiçbir yere enjekte edilmeyen "üst" sınıflara kadar uzun bir zincir oluşturur.

Gördüğüm tek yol şu an Injectoriçin tek birton aracılığıyla küresel olarak bir / uygulama bağlamı sağlamaktır , böylece diğer sınıflar ondan yönetilen fasulye alabilir. Ancak composition root, uygulamanın açıklanmaması konusunda önemli bir fikirle çelişmektedir .

Başka bir yaklaşım aşağıdan yukarıya olacaktır: "yüksek seviyeli" sınıflarla başlamak için, bunları bağımlılık enjeksiyon kabına dahil edin ve yavaşça "daha küçük" sınıflara geçin. Ama sonra uzun süre beklemek zorundayım, çünkü hala küresellere / statiklere bağlı olan daha küçük sınıfları test edebilirim.

Bu tür kademeli göçün yolu ne olabilir?

Not: Bağımlılık enjeksiyonuna kademeli yaklaşımlar başlığı benzerdir, ancak soruma cevap vermiyor.


1
Doğrudan sert bağlantılı sınıflara sahip olmaktan bağımlılık enjeksiyon kabı kullanmaya geçmek ister misiniz? Herhangi bir bağımlılık enjeksiyon çerçevesi veya aracıyla evlenmeyi düşünmeden önce arayüzleri kullanarak sınıfları ayırmak için bazı yeniden düzenleme yapmanız gerekir.
Tulains Córdova

Yeterince adil. Bu yüzden diğer 30 sınıfta kullanılan bir yardımcı sınıf A var (bunlardan biri sınıf B'dir), bu da onları test edilemez kılar. A sınıfını B sınıfının yapıcı bağımlılığı yapmayı düşündüm. Yapıcılara yapılan tüm çağrıları değiştirmeli ve A'yı içeriden geçirmeliyim. Ama nereden bulabilirim?
damluar

B, başlamak için DI'yi uygulayabileceğiniz en küçük birim ise, o zaman B'yi şu anda inşa ettiğiniz her yerde A yapımında herhangi bir zarar görmüyorum - A'nın bağımlılıkları olmaması şartıyla. Top yuvarlandığında, geri gelebilir ve yeniden düzenleyebilirsiniz.
Andy Hunt

@damluar Başlamak için bir fabrika mı?
Tulains Córdova

1
@damluar bir sınıf sadece bir fayda sınıfına bağlı olduğu için test edilemez hale gelmez. Bu, ünitenizin olmasını istediğinizden biraz daha büyük olduğu anlamına gelir. Fayda sınıfınızı kendi başına test edebiliyorsanız, endişelenmeden diğer 30 sınıf için de testlerde kullanabilirsiniz. Birim testi hakkında Martin Fowlers blogunu okuyun.
gbjbaanb

Yanıtlar:


2

Maalesef C#seçim benim dilidir, okuyabiliyorum Javaama muhtemelen bunu yazmaya çalışıyorum sözdizimi kasap olurdu ... Aynı kavramlar arasında geçerlidir C#ve Javaolsa da, bu yüzden umarım bu yavaş yavaş daha olmasını kod tabanını nasıl geçebileceğini adımları gösterecek test edilebilir.

Verilen:

public class MyUI
{
    public void SomeMethod()
    {
        Foo foo = new Foo();
        foo.DoStuff();
    }
}

public class Foo
{

    public void DoStuff()
    {
        Bar bar = new Bar();
        bar.DoSomethingElse();
    }

}

public class Bar
{
    public void DoSomethingElse();
}

bir IOC kabı kullanmadan DI'yi kullanmak için kolayca yeniden düzenlenebilir - ve hatta bunu birkaç adıma ayırabilirsiniz:

(potansiyel) Birinci adım - bağımlılıkları kabul edin, ancak arama (UI) kodunda değişiklik yapmayın:

public class MyUI
{
    public void SomeMethod()
    {
        Foo foo = new Foo();
        foo.DoStuff();
    }
}

public class Foo
{

    private IBar _iBar;

    // Leaving this constructor for step one, 
    // so that calling code can stay as is without impact
    public Foo()
    {
        _iBar = new Bar();
    }

    // simply because we now have a constructor that take's in the implementation of the IBar dependency, 
    // Foo can be much more easily tested.
    public Foo(IBar iBar)
    {
        _iBar = iBar;
    }

    public void DoStuff()
    {
        _iBar.DoSomethingElse();
    }

}

public interface IBar
{
    void DoSomethingElse();
}

public class Bar
{
    public void DoSomethingElse();
}

Refactor 2 (veya IOC kapsayıcısı uygulanır ve arama kodunu hemen değiştirirse ilk refactor):

public class MyUI
{
    public void SomeMethod()
    {
        Foo foo = null // use your IOC container to resolve the dependency
        foo.DoStuff();
    }
}

public class Foo
{

    private IBar _iBar;

    // note we have now dropped the "default constructor" - this is now a breaking change as far as the UI is concerned.
    // You can either do this all at once (do only step 2) or in a gradual manner (step 1, then step 2)

    // Only entry into class - requires passing in of class dependencies (IBar)
    public Foo(IBar iBar)
    {
        _iBar = iBar;
    }

    public void DoStuff()
    {
        _iBar.DoSomethingElse();
    }

}

Adım 2 teknik olarak kendi başına yapılabilir - ancak (potansiyel olarak) çok daha fazla iş olacaktır - şu anda kaç sınıfın DI için aradığınız işlevselliği "yenilerken".

Adım 1 -> adım 2 rotası ile devam etmeyi düşünün - için Foobağımsız olarak birim testleri oluşturabilirsiniz Bar. 1. adım refactordan önce, her iki sınıfın gerçek uygulaması kullanılmadan kolayca gerçekleştirilemedi. Adım 1 -> adım 2 (hemen adım 2 yerine), zaman içinde daha küçük değişikliklere izin verir ve refactor'unuzun sonuçsuz çalışmasını sağlamak için zaten bir test kayışına başlayabilirsiniz.


0

Java, PHP ve hatta C # kullansanız da konsept aynıdır. Bu soru, bu YouTube videosunda Gemma Anible tarafından oldukça iyi ele alınmıştır:

https://www.youtube.com/watch?v=Jccq_Ti8Lck (PHP, üzgünüm!)

Test edilemeyen kodu, yeni, test edilebilir kodu çağıran "cephe" (daha iyi bir terim olmaması nedeniyle) ile değiştirirsiniz. Sonra yavaş yavaş eski çağrıları enjekte edilen servislerle değiştirebilirsiniz. Bunu geçmişte yaptım ve oldukça iyi çalışıyor.

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.