Basit DI kodunun aksine neden bir IoC konteynerine ihtiyacım var? [kapalı]


598

Bir süredir bir yapıcı, özellik veya yöntem enjekte ederek Bağımlılık Enjeksiyonu (DI) kullanıyorum . Hiç bir Inversion of Control (IoC) konteyneri kullanmaya ihtiyaç duymadım . Bununla birlikte, ne kadar çok okursam, topluluktan bir IoC kapsayıcısı kullanması için o kadar fazla baskı hissediyorum.

StructureMap , NInject , Unity ve Funq gibi .NET kaplarıyla oynadım . Bir IoC konteynerinin koduma nasıl fayda sağlayacağını / geliştireceğini hala göremiyorum.

Ayrıca işyerinde bir konteyner kullanmaya başlamaktan korkuyorum çünkü iş arkadaşlarımın çoğu anlamadıkları kodu görecekler. Birçoğu yeni teknoloji öğrenmek konusunda isteksiz olabilir.

Lütfen, bana IoC kabı kullanmam gerektiğine ikna et. Bu argümanları işteki geliştiricilerimle konuştuğumda kullanacağım.


6
İyi soru. IOC fikrinde çok yeni olduğum için bunu yorumlarda cevaplıyorum. Tak ve çalıştır bileşenleri ve gevşek bağlantı fikri gibi görünüyor. Mevcut bileşen yerine başka bir bileşen kullanmanız gerekip gerekmediği, ihtiyaca dayalıdır. IOC kullanımı, IMO ortaya çıkarsa, bu tür bir değişikliğin kodunu hazırlamaktır.
shahkalpesh

3
@shahkalpesh ama basit DI ile gevşek bağlantı elde edebilirsiniz. Ancak, yapılandırma dosyasını kullanmamın sebebini anlıyorum. Ancak, yapılandırma dosyasını kullanmak isteyip istemediğimden emin değilim. Yapılandırma dosyaları çok ayrıntılı, yeniden düzenleme ile ilgili zorluklar ve birkaç dosya arasında geçiş yapma.
Vadim

110
esas olarak IoC'yi kullanmak için nedenler aradığınız anlaşıldığı için bir yorum eklemek; ancak sorununuzla ilgili başka bir şey söylenmelidir. Kodunuzu ekibinizdeki en kötü kişi seviyesine veya öğrenmek istemeyeceklerinden korktuğunuz için yazamazsınız. Sadece profesyonel olmakla kalmayıp, aynı zamanda büyümek için geleceğinize karşı da sorumluluğunuz vardır ve ekibiniz sizi geride bırakmamalıdır.
Kevin Sheffield

4
@Kevin, Aslında IoC kullanıyoruz. Herkese IoC'yi öğretmek sanıldığı kadar zor değildi.
Vadim

21
Moderatörlerin neden kitlesel olarak onaylanmış ve faydalı soruları kapadıkları hakkında hiçbir fikrim yok. Belki de bu durumda, Will soruyu anlamıyor ya da insanların stackexchange'i ne için kullandığını anlamıyor? Eğer "stackexchange'in Soru-Cevap formatı için iyi bir uyum" değilse, belki de politikanızın yanlış olduğunu ve yüzlerce oylama ve oylanan faydalı cevaplarla yüzlerce yararlı sorunun tümünü düşünmeyin.
NickG

Yanıtlar:


441

Vay canına, Joel'in bunu tercih edeceğine inanamıyorum:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

bunun üzerinde:

var svc = IoC.Resolve<IShippingService>();

Birçok kişi bağımlılık zincirinizin iç içe olabileceğini fark etmez ve bunları manuel olarak bağlamak hızlı bir şekilde gereksiz hale gelir. Fabrikalarda bile, kodunuzun kopyalanması buna değmez.

IoC kapları karmaşık olabilir, evet. Ama bu basit durum için inanılmaz derecede kolay olduğunu gösterdim.


Tamam, bunu daha da gerekçelendirelim. Akıllı kullanıcı arayüzüne bağlamak istediğiniz bazı varlıklarınız veya model nesneleriniz olduğunu varsayalım. Bu akıllı kullanıcı arayüzü (buna Shindows Morms adını vereceğiz) INotifyPropertyChanged uygulamasını uygulamanızı istiyor, böylece değişiklik izleme yapabilir ve kullanıcı arayüzünü buna göre güncelleyebilir.

"Tamam, bu kulağa pek hoş gelmiyor" diye yazmaya başlıyorsun.

Bununla başlıyorsunuz:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

..ve ile bitirmek bu :

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Bu iğrenç sıhhi tesisat kodu, ve eğer böyle bir kod yazıyorsanız , müşterinizden çaldığınızı iddia ediyorum . Daha iyi, daha akıllı bir çalışma şekli var.

Hiç bu terimi duydun, daha akıllıca çalış, zor değil mi?

Takımınızdaki bazı akıllı adamların gelip şöyle dediğini hayal edin: "İşte daha kolay bir yol"

Mülklerinizi sanal yapılırsa o zaman olabilir (sakin aşağı, bir anlaşma büyük değil) örgü otomatik olarak bu özellik davranış. (Buna AOP denir, ancak ad için endişelenmeyin, sizin için ne yapacağına odaklanın)

Hangi IoC aracını kullandığınıza bağlı olarak, şuna benzer bir şey yapabilirsiniz:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

Puf! Tüm bu INotifyPropertyChanged BS kılavuzu, söz konusu nesnenin her sanal özellik ayarlayıcısında sizin için otomatik olarak oluşturulur.

Bu sihir mi? EVET ! Bu kodun işini gerçekleştirdiğine güvenebiliyorsanız, mumbo-jumbo'yu sarmalayan tüm bu özellikleri güvenle atlayabilirsiniz. Çözmeniz gereken iş problemleriniz var.

AOP yapmak için bir IoC aracının diğer bazı ilginç kullanımları:

  • Bildirici ve iç içe veritabanı işlemleri
  • Beyan ve iç içe çalışma birimi
  • Kerestecilik
  • Ön / Son koşullar (Sözleşmeye Göre Tasarım)

28
Hata! Tüm özellikleri sanallaştırmayı unuttun ve işe yaramadı. Bu çalmayı hata ayıklamak için istemcinizden de ayırmanız gereken zaman mı? (DynamicProxy kullanmıyorsanız özür dileriz.: P)
Thom

17
INotifyPropertyChanged sarmalayıcısını kullanarak çok fazla netlik feda edersiniz. Başlamak için bu sıhhi tesisatın yazılması birkaç dakikadan fazla sürerse yanlış işin içindesiniz. Kodunuzun ayrıntı düzeyi söz konusu olduğunda daha az değildir!
overstood

39
@overstood - Tamamen katılmıyorum. İlk olarak, bu örnekte netlik nerede önemlidir? En yakın tüketici. Tüketici açıkça INotifyPropertyChanged istiyor. Biz sırf edebilir mikro veya makro kod üretimi ile bu kod türünü oluşturmak, bunu yazmak için iyi bir fikir olduğu anlamına gelmez. Bu INotifyPropertyChanged gunk sadece çürük, sıradan, sıhhi tesisattır ve aslında söz konusu sınıfın okunabilirliğinden uzaklaşır.
Ben Scheirman

31
Bağımlılık Enjeksiyon kalıbını ve Servis Bulucu kalıbını karıştırdığınız için işaretlendi. Birincisi ikincisi yerine kullanılmak üzere tasarlanmıştır. Örneğin, bir nesne (veya tüm sistemin tamamı) asla IShippingService örneğini almak için Resolve (...) öğesini çağırmamalıdır. IShippingService'e ihtiyaç duyan nesneler, oluşturulduklarında kullanmaları gereken örneğe bir başvuru almalıdır ve iki nesneyi birbirine bağlamaktan daha yüksek bir katman sorumludur.
Nat

30
Bu ne olursa olsun (IoC, DynamicProxy, AOP veya kara büyü ) birileri beni somut bir makaleye / bunu başarmak için daha fazla ayrıntı sağlayan belirli bir belgeye bağlayabilir mi?
fostandy

138

Seninleyim Vadim. IoC kapları basit, zarif ve kullanışlı bir konsept alır ve 200 sayfalık bir kılavuzla iki gün boyunca çalışmak zorunda olduğunuz bir şey haline getirir.

Ben şahsen, IoC topluluğunun Martin Fowler tarafından güzel ve zarif bir makaleyi nasıl aldığını ve tipik olarak 200-300 sayfa kılavuzuyla bir dizi karmaşık çerçeveye dönüştürdüğünü şaşırdım.

Yargılamaya çalışmamaya çalışıyorum (HAHA!), Ancak bence IoC kapları kullanan insanlar (A) çok akıllı ve (B) kadar akıllı olmayan insanlar için empati kuruyorlar. Her şey onlar için mükemmel bir anlam ifade ediyor, bu yüzden birçok sıradan programcının kavramları kafa karıştırıcı bulacağını anlamakta zorlanıyorlar. Bu bilginin laneti . IoC kaplarını anlayan insanlar, onu anlamayan insanlar olduğuna inanmakta zorluk çekiyorlar.

IoC kapsayıcı kullanmanın en değerli yararı, tek bir yerde, örneğin test modu ve üretim modu arasında geçiş yapmanızı sağlayan bir yapılandırma anahtarına sahip olmanızdır. Örneğin, veritabanı erişim sınıflarınızın iki sürümüne sahip olduğunuzu varsayın ... bir sürüm agresif bir şekilde günlüğe kaydedilen ve geliştirme sırasında kullandığınız çok fazla doğrulama gerçekleştiren bir sürüm ve giriş için çığlık atmayan hızlı bir şekilde üretim yapmadan başka bir sürüm. Aralarında bir yerde geçiş yapabilmek güzel. Öte yandan, bu IoC kaplarının karmaşıklığı olmadan daha basit bir şekilde kolayca halledilebilen oldukça önemsiz bir sorundur.

IoC kapları kullanırsanız, kodunuzun açıkçası, okunması çok daha zor hale geldiğine inanıyorum. Kodun ne yapmaya çalıştığını anlamak için bakmanız gereken yer sayısı en az bir artar. Ve cennetin bir yerinde bir melek ağlıyor.


81
Bence gerçeklerin biraz Joel var. İlk önce IoC ve konteynırlar geldi, daha sonra tam tersi değil Martin tarafından yapıldı.
Glenn Block

55
eesh kapların çoğu çok hızlı bir şekilde devam etmenizi sağlar. Autofac, yapı haritası, birlik, ninject, vb ile konteyner yaklaşık 5 dakika içinde çalışması mümkün olmalıdır. Evet, gelişmiş özelliklere sahipler ama yerden çıkmak için bunlara ihtiyacınız yok.
Glenn Block

58
Joel seninle aynı şekilde düşünürdüm. Sonra tam anlamıyla ve ciddi bir şekilde en temel kurulumun nasıl çalıştırılacağını anlamak için beş dakika geçirdim ve 80/20 kuralını uygularken hayrete düştüm. Özellikle basit durumlarda kodu çok daha temiz hale getirir.
Justin Bozonier

55
İki yıldan az bir süredir program yapıyorum ve Ninject wiki'sini okudum ve anladım. O zamandan beri DI'yi birçok yerde kullanıyorum. Şunu gördün mü? İki yıldan az ve wiki'de birkaç sayfa okuma. Ben bir sihirbaz falan değilim. Kendimi bir dahi olarak görmüyorum, ama anlaması kolaydı. OO'yu gerçekten anlayan birinin onu kavrayamadığını hayal edemiyorum. Ayrıca, hangi 200-300 sayfa kılavuzuna atıfta bulunuyorsunuz? Bunları hiç görmedim. Kullandığım tüm IoC kapları oldukça kısa ve nokta.
JR Garcia

35
+1 iyi yazılmış ve düşünülmüş. Bir çok insanın farkına varmadığını düşündüğüm bir şey, bir şeyi "sihirli bir şekilde" işlemek için bir çerçeve veya benzeri eklemenin bir sisteme karmaşıklık ve sınırlamalar eklediğidir. Bazen buna değer, ancak çoğu zaman bir şey göz ardı edilir ve sınırlamalar tarafından zorlanan bir sorunu düzeltmeye çalışan koda entropi serbest bırakılır. Önden zaman kazandırabilir, ancak insanlar sadece bir avuç insan gerçekten çözmek için yeterli bilgiye sahip bazı belirsiz sorunu düzeltmeye çalışırken çizginin 100 katına mal olabileceğini fark etmelidir.
kemiller2002

37

Muhtemelen hiç kimse sizi DI kap çerçevesi kullanmaya zorlamıyor. Sınıflarınızı ayırmak ve test edilebilirliği artırmak için zaten DI'yi kullanıyorsunuz, böylece birçok avantaj elde edersiniz. Kısacası, genellikle iyi bir şey olan sadeliği tercih ediyorsunuz.

Sisteminiz, manuel DI'nin bir angarya haline geldiği (yani bakımı artırdığı) bir karmaşıklık düzeyine ulaşırsa, bunu bir DI konteyner çerçevesinin ekip öğrenme eğrisine göre tartın.

Bağımlılık yaşam boyu yönetimi üzerinde daha fazla kontrole ihtiyacınız varsa (yani, Singleton modelini uygulama gereğini hissediyorsanız), DI kaplarına bakın.

Bir DI kabı kullanıyorsanız, yalnızca ihtiyacınız olan özellikleri kullanın. XML yapılandırma dosyasını atlayın ve yeterliyse kodda yapılandırın. Yapıcı enjeksiyonuna yapışır. Unity veya StructureMap'in temelleri birkaç sayfaya yoğunlaştırılabilir.

Mark Seemann'ın bu konuda harika bir blog yazısı var: DI Container ne zaman kullanılır?


Çok iyi yanıt. Görüşte farklılık göstereceğim tek şey, manuel enjeksiyon EVER'in daha az karmaşık olup olmadığı veya bir angarya olup olmadığıdır.
wekempf

+1. Burada en iyi cevaplardan biri. Blog makalesi bağlantısı için de teşekkürler.
stakx - artık

1
Dengeli bakış açısı için +1. IoC kapları her zaman iyi değildir ve her zaman kötü değildir. Bir ihtiyaç görmüyorsanız, kullanmayın. Bir ihtiyaç görürseniz aracın orada olduğunu bilin.
Phil

32

Kanımca, bir IoC'nin bir numaralı yararı, bağımlılıklarınızın yapılandırmasını merkezileştirme yeteneğidir.

Şu anda Bağımlılık enjeksiyonunu kullanıyorsanız kodunuz şöyle görünebilir

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

IMHO daha karmaşık, yapılandırma dosyalarının aksine statik bir IoC sınıfı kullandıysanız, şöyle bir şey olabilir:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Sonra, Statik IoC sınıfınız şöyle görünecektir, burada Unity kullanıyorum.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}

11
Ben, bahsettiğiniz şey Bağımlılık Enjeksiyonu değil, gerçekten Servis Konumu - bir fark var. Ben her zaman ikincisini tercih ederim. stevenharman.net/blog/archive/2009/09/25/…
stevenharman

23
IoC.Resolve <T> öğesini varsayılan kurucunuzda yapmak yerine, IOC kapsayıcısının nesneyi sizin için oluşturma yeteneğinden yararlanmalısınız. Bu boş kurucudan kurtulun ve IOC kapsayıcısının nesneyi sizin için oluşturmasına izin verin. var presenter = IoC.Resolve <CustomerPresenter> (); işte! tüm bağımlılıklar zaten kablolu.
Ben Scheirman

12
Bu tür bir konfigürasyonun merkezileştirilmesinin alt tarafı, bilginin bağlam dışılaştırılmasıdır. Joel bu sorunun "açıkçası, okunması çok daha zor hale geldiğini" söylerken dikkat çekiyor.
Scott Bellware

6
@sbellware, ne bilgisi? Birincil bağımlılık olarak alınan arayüz, bir sözleşme görevi görür ve hem amacını hem de davranışını iletmek için yeterli olmalıdır, değil mi? Sanırım, sözleşmenin uygulanmasına girmekten kazanılan bilgilere atıfta bulunuyorsunuz, bu durumda uygun yapılandırmayı izlemeniz gerekir? Ben bir grafik (VS R, VS, IntelliJ, vb) bir grafiğin düğümleri ve kenarları gezinme büyük olduğu için güveniyor; Beynimin yığınını şu anda odaklandığım çalışma alanı bağlamında saklıyorum.
stevenharman

7
Steve, .NET metnini biliyorum ve yıllarca yürüdüm. Diğer modlara ve formlara da yakından aşinayım ve içtenlikle gerçek ödünleşmeler olduğunu anlıyorum. Bu ödünleşmeleri nasıl ve ne zaman yapacağımı biliyorum, ama çalışma hayatımdaki çeşitlilik en azından Joel'in bakış açısını somut bir şekilde anlamama izin veriyor. Çoğu .NET mono-kültürcüsünden daha uzun süredir kullandığım araçlar ve püf noktaları hakkında konuşmak yerine, bilmediğim bir şey söyle. Beynimin yığınını alt.net stasis ve ortodoksiden ziyade farklı, eleştirel düşünme için saklıyorum.
Scott Bellware

32

IoC Kapları, derin yuvalanmış sınıf bağımlılıklarını yüklemek için de iyidir. Örneğin, Depedency Injection kullanarak aşağıdaki kodu aldıysanız.

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

Tüm bu bağımlılıkları ve IoC kapsayıcısını yüklediyseniz, CustomerService'i Çözebilirsiniz ve tüm alt bağımlılıklar otomatik olarak çözülür.

Örneğin:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}

Ancak IoC sınıfı, servis bulucu anti-paterni kullanılarak çağrılır. _Konteyner statik T Resolve <T> () öğesini çağırmak yerine DI yapıcısı kullanılarak geçirilmelidir mi?
Michael Freidgeim

@bendewey Örneğiniz, argümanın otomatik olarak yeni CustomerService ve yeni CustomerRepository için yapıcılara iletildiğini ima eder. Bu böyle mi çalışıyor? Başka ek parametreleriniz varsa ne olur?
Brain2000

28

Ben deklaratif programlama hayranıyım (cevapladığım SQL sorularına bakın), ancak baktığım IoC kapları kendi iyilikleri için çok gizemli görünüyor.

... ya da belki IoC kapsayıcılarının geliştiricileri net belgeler yazamazlar.

... ya da her ikisi de bir dereceye kadar doğrudur.

IoC konteyneri kavramının kötü olduğunu düşünmüyorum . Ancak uygulama, çok çeşitli uygulamalarda yararlı olacak kadar güçlü (yani esnek) olmalı, ancak basit ve kolay anlaşılabilir olmalıdır.

Muhtemelen diğerinden altı buçuk düzine. Gerçek bir uygulama (bir oyuncak ya da demo değil), birçok köşe vakası ve kuralların istisnalarını hesaba katarak karmaşık olmak zorundadır. Bu karmaşıklığı zorunlu kodda ya da bildirim kodunda kapsüllersiniz. Ama onu bir yerde temsil etmelisin.


5
Kaçınılmaz karmaşıklığın varlığı hakkındaki gözleminizi seviyorum. Konteyner tabanlı ve manuel bağımlılık kabloları arasındaki fark, bir konteyner kullanarak, her bir bileşene ayrı ayrı odaklanabilmeniz ve böylece artan karmaşıklığı oldukça doğrusal bir şekilde yönetebilmenizdir. Manuel bağımlılık kablolarına sahip sistemlerin gelişmesi daha zordur, çünkü bir bileşeni yapılandırma eyleminin diğer her şeyin yapılandırılmasından izole edilmesi zordur.
Nicholas Blumhardt

AspectJ'yi seviyorum. Java değil, XML de değil. Bir çeşit IS Java, bir uzantısı olarak Java'yı hissediyor ve benziyor. Bakış açılarımı POJO'larımdan ve iş mantığımdan uzak tutmayı tercih ederim. Neredeyse IoC'lerimi de dışarıda tutmak istiyorum. Kablolama araçlarında tamamen çok fazla XML var, IMHO. Yapılandırma dosyalarına sahip olmam gerekiyorsa, BeanShell komut dosyaları olsun. / Java, .Net çalışmalarınızı buraya uygulayın ->.
Chris K

2
Cevabınızı kesinlikle anlıyorum, ancak sorunun büyük bir kısmının, IoC çerçevelerinin birçoğunun (ve gerçekten de diğer şeyler için diğer çerçevelerin birçoğunun) zaten çok aşina olan diğerleri için tanımlandığını ve belgelendiğini düşünüyorum. gerekli kavramlar ve terminoloji. Ben sadece başka bir programcının kodunu miras almış bir sürü öğreniyorum. 'Nesne grafikleri' oldukça küçük olduğunda ve kodun gerçek bir test kapsamı olmadığında kodun neden IoC kullandığını anlamak için uğraşıyorum. Bir SQL programcısı olarak IoC'yi ORM ile kullanmayı muhtemelen daha iyi takdir edebilirsiniz.
Kenny Evitt

1
@KennyEvitt, iyi bir nokta, eğer basit bir uygulaması varsa ve iyi bir test kapsamına sahip olmak için bile rahatsız etmediyse, IoC çerçevesini kullanmak için erken görünüyor.
Bill Karwin

27

Bir kapsayıcı kullanmak çoğunlukla zorunlu / betikli başlatma ve yapılandırma stilinden bildirimsel olana dönüştürmektir. Bunun birkaç farklı faydalı etkisi olabilir:

  • Hairball ana program başlangıç ​​rutinlerini azaltmak .
  • Oldukça derin dağıtım zamanı yeniden yapılandırma yeteneklerini etkinleştirir.
  • Bağımlılık enjekte edilebilir stili yeni işler için en az direnç yolu haline getirmek.

Elbette zorluklar olabilir:

  • Karmaşık başlatma / kapatma / yaşam döngüsü yönetimi gerektiren kod, bir kaba kolayca uyarlanamayabilir.
  • Muhtemelen kişisel, süreç ve takım kültürü konularında gezinmeniz gerekecek - ama o zaman, bu yüzden sordunuz ...
  • Bazı araç takımları hızla ağırlaşıyor ve birçok DI konteynerinin karşı tepki olarak başlattığı derin bağımlılığı teşvik ediyor.

23

Gibi bana geliyor zaten kendi IoC kapsayıcı inşa ve başkasının uygulaması daha iyi seninkinden daha neden soruyorsun (Martin Fowler tarafından tarif edildi değişik kalıplarını kullanarak).

Yani, zaten çalışan bir grup kodunuz var. Ve neden bir başkasının uygulamasıyla değiştirmek istediğinizi merak ediyorsunuz.

Üçüncü taraf IoC kapsayıcısını dikkate aldıkları için profesyoneller

  • Ücretsiz olarak hata düzeltmeleri alıyorsunuz
  • Kütüphane tasarımı sizinkinden daha iyi olabilir
  • İnsanlar belirli bir kütüphaneye zaten aşina olabilir
  • Kütüphane sizinkinden daha hızlı olabilir
  • Uygulamanızı istediğiniz ancak hiç zaman ayırmadığınız bazı özelliklere sahip olabilir (bir servis noktanız var mı?)

Eksileri

  • Ücretsiz olarak hatalar tanıtıyorsunuz :)
  • Kütüphane tasarımı sizinkinden daha kötü olabilir
  • Yeni bir API öğrenmelisiniz
  • Asla kullanmayacağınız çok fazla özellik
  • Yazmadığınız kodda hata ayıklamak genellikle daha zordur
  • Önceki bir IoC kapsayıcısından taşıma yorucu olabilir

Yani, artılarınızı eksilerinize karşı tartın ve bir karar verin.


Sana katılıyorum Sam - DI bir çeşit IoC'dir, bu yüzden orijinal poster DI yapıyorsa, zaten IoC yapıyorlar, bu yüzden gerçek soru neden kendinize ait olmanız.
Jamie Love

3
Katılmıyorum. IOC, DI yapmanın bir yoludur. IOC Kapsayıcıları, bağımlılıkları karşılamak ve enjekte etmek için dinamik çalışma zamanı yansıması kullanarak tür örneklemesinin denetimini alarak DI yapar. OP, statik DI yaptığını öne sürüyor ve neden IOC'nin dinamik çalışma zamanı yansımasıyla ilişkili faydalar için derleme zamanı kontrolünün faydalarını takas etmesi gerektiğini düşünüyor.
Maxm007

17

Sanırım bir IoC'nin değerinin büyük kısmı DI kullanılarak kazanılıyor. Bunu zaten yaptığınız için, geri kalan fayda artımlıdır.

Alacağınız değer, üzerinde çalıştığınız uygulamanın türüne bağlı olacaktır:

  • Çoklu kiracı için IoC kapsayıcısı, farklı istemci kaynaklarını yüklemek için bazı altyapı kodlarıyla ilgilenebilir. İstemciye özgü bir bileşene ihtiyacınız olduğunda, mantığı işlemek için özel bir seçici kullanın ve istemci kodunuzdan endişelenmeyin. Bunu kesinlikle kendiniz oluşturabilirsiniz, ancak bir IoC'nin nasıl yardımcı olabileceğinin bir örneği .

  • Pek çok genişletilebilirlik noktasıyla IoC, bileşenleri yapılandırmadan yüklemek için kullanılabilir. Bu inşa etmek yaygın bir şeydir, ancak araçlar konteyner tarafından sağlanır.

  • Bazı kesişen endişeler için AOP'yi kullanmak istiyorsanız, IoC yöntem çağrılarına müdahale etmek için kancalar sağlar . Bu, projelerde daha az yaygın olarak yapılır, ancak IoC bunu kolaylaştırır.

Daha önce böyle bir işlevsellik yazdım, ancak şimdi bu özelliklerden herhangi birine ihtiyacım varsa, mimarime uygunsa önceden oluşturulmuş ve test edilmiş bir araç kullanmayı tercih ederim.

Başkaları tarafından belirtildiği gibi, kullanmak istediğiniz sınıfları merkezi olarak da yapılandırabilirsiniz. Bu iyi bir şey olsa da, yanlış yönlendirme ve komplikasyon maliyeti vardır. Çoğu uygulama için temel bileşenler çok fazla değiştirilmemiştir, bu nedenle takas yapmak biraz daha zordur.

Bir IoC kapsayıcısı kullanıyorum ve işlevselliği takdir ediyorum ama değiş tokuşu fark ettiğimi itiraf etmeliyim: Kodum sınıf düzeyinde daha net ve uygulama düzeyinde daha az netleşiyor (yani kontrol akışını görselleştirmek).


16

İyileştirici bir IOC bağımlısıyım. Bu günlerde çoğu durumda DI için IOC kullanmayı haklı çıkarmak zor buluyorum. IOC kapları derleme zamanı kontrolünü feda eder ve karşılığında "kolay" kurulum, karmaşık ömür yönetimi ve çalışma anında bağımlılıkların anında keşfedilmesini sağlar. Derleme zamanı kaybını ve sonuçta ortaya çıkan çalışma süresini büyü / istisnalar buluyorum, vakaların büyük çoğunluğunda çan ve ıslıklara değmez. Büyük kurumsal uygulamalarda olup bitenleri takip etmeyi çok zorlaştırabilirler.

Merkezileştirme argümanını satın almıyorum çünkü uygulamanız için soyut bir fabrika kullanarak ve nesne oluşturma işlemini soyut fabrikaya dini olarak erteleyerek yani uygun DI yapın.

Neden statik sihirli ücretsiz DI böyle yapmıyorsunuz:

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}


interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }


    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}


namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

Kapsayıcı yapılandırmanız soyut fabrika uygulamanızdır, kayıtlarınız soyut üyelerin uygulamalarıdır. Yeni bir singleton bağımlılığına ihtiyacınız varsa, soyut fabrikaya başka bir soyut özellik eklemeniz yeterlidir. Geçici bir bağımlılığa ihtiyacınız varsa, başka bir yöntem ekleyin ve bir Func <> olarak enjekte edin.

Avantajları:

  • Tüm kurulum ve nesne oluşturma yapılandırması merkezileştirilmiştir.
  • Yapılandırma sadece koddur
  • Derleme zamanı kontrolü, kayıtlarınızı güncellemeyi unutamayacağınız için bakımı kolaylaştırır.
  • Çalışma zamanı yansıma sihri yok

Şüphecileri bir sonraki yeşil alan projesine vermelerini ve dürüstçe konteynere hangi noktada ihtiyacınız olduğunu sormanızı öneririm. Bir fabrika uygulamasını bir IOC Container yapılandırma modülüyle değiştirdiğiniz için, daha sonra bir IOC kapsayıcısını hesaba katmak kolaydır.


Bu bana IoC'ye karşı bir argüman değil, yapılandırılabilir IoC çerçevelerine karşı bir argüman gibi geliyor . Buradaki fabrikalarınız sadece basit kodlanmış IoC'lere kaybolmuyor mu?
Mr.Mindor

Poster, IoC'den bahsettiğinde bir IoC kap çerçevesi anlamına geldiğini düşünüyorum. Orijinal yazım için teşekkürler, bu beni çerçevesiz gitmeye ikna etmeme yardımcı oluyor. Bu yüzden testlerde ve üretim kodunda yapılandırılabilirse, "yapılandırılabilir" bir çerçevenin avantajı nedir?
huggie

Terminoloji anlayışımdan, Tersine Çevirme, çalışma zamanında bağımlılık bulmak anlamına gelir. Yani evet, fabrikanın temelde sabit kodlu veya statik bir kap olduğunu söyleyebilirsiniz, ancak bu bir IOC kabı değildir. Türler derleme zamanında çözümlenir. Çalışma zamanında türleri çözümlemek için sözlük aramaları kullanan yapılandırılabilir çerçevelerin kullanılmasına karşı bir argüman.
Maxm007

14

Benim için IoC kapları kullanmanın en büyük yararı (kişisel olarak Ninject kullanıyorum), ortamların ve diğer küresel devlet nesnelerinin geçişini ortadan kaldırmak oldu.

Web için programlamıyorum, benimki bir konsol uygulaması ve nesne ağacının derinliklerinde birçok yerde, nesne ağacının tamamen ayrı bir dalında oluşturulan kullanıcı tarafından belirlenen ayarlara veya meta verilere erişmem gerekiyor. IoC ile Ninject'e Ayarları tek bir şekilde ele almasını söylüyorum (çünkü her zaman sadece bir örneği var), yapıcıda Ayarlar veya Sözlük isteyin ve onlara ihtiyacım olduğunda sihirli bir şekilde görünürler!

IoC kapsayıcısı kullanmadan ayarları ve / veya meta verileri, gerçekten ihtiyaç duyulan nesne tarafından kullanılmadan önce 2, 3, ..., n nesnelerinden geçirmem gerekirdi.

DI / IoC konteynırlarının diğer insanların burada detaylandırdığı ve nesneler oluşturma fikrinden nesnelere talep etme fikrine geçmenin akıllara zarar verebileceği birçok faydası var, ancak DI'yi kullanmak benim ve ekibim için çok yararlı oldu, belki de ekleyebilirsiniz. cephaneliğinize!


2
Bu büyük bir artı. Daha önce kimsenin bahsetmediğine inanamıyorum. Geçici nesneleri geçirme veya saklama ihtiyacı olmadan kodu çok daha temiz hale getirir.
Finglas

1
@ qble global nesneleri harika çalışıyor ... kod tabanınızın zaman içinde değiştirilmesinin zorlaştığı sıkıca bağlı, test edilemeyen bir kabus olmasını istiyorsanız. iyi şanslar.
Jeffrey Cameron

2
@JeffreyCameron IoC Konteyner olan küresel nesne. Global bağımlılıklarınızı ortadan kaldırmaz.
qble

3
Doğru, ancak IoC kapsayıcısı uygulamanın geri kalanına saydamdır. Bir örnek almak için başlangıçta bir kez çağırırsınız ve sonra bunu en son görürsünüz. Global nesnelerle kodunuz zor bağımlılıklarla doludur
Jeffrey Cameron

1
@ qble, fabrikaları anında geçici örnekler oluşturmak için kullanır. Fabrikalar tekil hizmetlerdir. Geçici eşgörünümlerin IOC kabından bir şeye ihtiyacı varsa, bunları fabrikaya bağlayın ve fabrikanın bağımlılığı geçici örneğe geçirmesini sağlayın.
Jeffrey Cameron

14

IoC çerçeveleri mükemmel ...

  • ... tip güvenliği atın. Birçok (tümü?) IoC çerçevesi, her şeyin doğru şekilde bağlandığından emin olmak istiyorsanız, kodu yürütmeye zorlar. "Hey! Umarım her şeyi ayarladım, bu yüzden bu 100 sınıftaki başlatmalarım üretimde başarısız olmayacak, boş gösterici istisnaları atacak!"

  • ... globaller ile çöp kodunuzu (IoC çerçeveler vardır tüm genel durumları değiştirme hakkında).

  • ... neyin neye bağlı olduğunu asla bilemeyeceğiniz için, yeniden yapılandırılması zor olan belirsiz bağımlılıklar içeren boktan kodlar yazın.

IoC ile ilgili sorun, onları kullanan insanların böyle kod yazmasıydı

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

Foo ve Bar arasındaki bağımlılık kablolu olduğu için açıkça kusurludur. Sonra kod yazmanın daha iyi olacağını fark ettiler

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

ki bu da kusurludur, ama daha az açıktır. Haskell'de türü Foo()olurdu IO Fooama gerçekten IO-part istemezsiniz ve eğer varsa tasarımınızla ilgili bir şeyin yanlış olduğuna dair bir uyarı işareti olmalıdır.

Ondan kurtulmak için (IO kısmı), IoC çerçevelerinin tüm avantajlarından yararlanın ve bunun dezavantajlarından hiçbirini soyut bir fabrika kullanamazsınız.

Doğru çözüm,

data Foo = Foo { apa :: Bar }

ya da belki

data Foo = forall b. (IBar b) => Foo { apa :: b }

ve enjekte et (ama buna enjekte demem) Bar.

Ayrıca: DI Me'nin matematik bilmeyen insanlar için olduğunu ve daha fazla anlaşamadığımı söyleyen Erik Meijer (LINQ mucidi) ile bu videoyu izleyin: http://www.youtube.com/watch?v = 8Mttjyf-8P4

Bay Spolsky'nin aksine IoC çerçevelerini kullanan insanların çok akıllı olduğuna inanmıyorum - sadece matematik bilmediklerine inanıyorum.


9
Tip güvenliğini atın - örneğin hala güvenli tipte olması dışında, arayüz budur. Nesne türlerinden değil, arayüz türünden geçiyorsunuz. Ve tip güvenliği null referans istisnaları dışında zaten ortadan kalkmaz, null referansları tip güvenli parametreler olarak mutlu edebilirsiniz - bu bir tür güvenlik sorunu değildir.
blowdart

IoC konteynerinde tipin kablolu olup olmadığını bilmediğiniz için tip güvenliğini atıyorsunuz (IoC tipi <IBar> değil IBaraslında IO IBar) .. Ve evet, Haskell örneğinde Bir boş başvuru alamıyorum.
finnsson

Gerçekten güvenliği kablolanmış bir nesne ile ilgili değil, en azından Java / C # 'da değil, sadece doğru tipte bir nesne ile ilgilidir. ConcreteClass myClass = null hala doğru türde. Eğer null değil zorlamak istiyorsanız kod sözleşmeleri devreye girer.
blowdart

2
Bir noktanıza bir dereceye kadar katılıyorum, 2'inci bir açıklama istiyorum ve 3'üncü benim için hiç sorun olmadı. C # örneğiniz IoC kapsayıcısını yaygın bir hata olan bir servis bulucu olarak kullanır. Ve C # ile Haskell karşılaştırması = elma vs portakal.
Mauricio Scheffer

2
Tip güvenliğini atmazsınız. Bazı (çoğu değilse) DI kapları, kayıt işleminden sonra tüm nesne grafiğini doğrulamanızı sağlar. Bunu yaparken uygulamanın yanlış yapılandırmaya (hızlı başarısız) başlamasını önleyebilirsiniz ve uygulamalarımda test sırasında bu hataları bulmama izin vermek için üretim yapılandırmasını çağıran birkaç birim sınama var. Tüm üst düzey nesnelerin çözülebildiğinden emin olmak için birkaç birim test yazması için IoC kapsayıcısı kullanan herkese tavsiye ederim.
Steven

10

Bağımlılık Enjeksiyonunun doğru bir şekilde uygulanmasının programcıları kodun test edilebilirliğini, esnekliğini, sürdürülebilirliğini ve ölçeklenebilirliğini geliştirmeye yardımcı olan çeşitli diğer programlama uygulamalarını kullanmaya zorladığını gördüm: Tek Sorumluluk İlkesi, Endişelerin Ayrılması ve kodlama API'lara karşı. Daha modüler, ısırık büyüklüğünde sınıflar ve yöntemler yazmak zorunda kalıyormuşum gibi geliyor, bu da kodun okunmasını kolaylaştırıyor, çünkü ısırık büyüklüğünde parçalar halinde alınabiliyor.

Ancak aynı zamanda, bir çerçeveden (özellikle sözleşmeler kullanıyorsanız) elle olduğundan çok daha kolay yönetilen oldukça büyük bağımlılık ağaçları oluşturma eğilimindedir. Bugün LINQPad'de bir şeyi gerçekten hızlı bir şekilde test etmek istedim ve modüllerimde bir çekirdek oluşturmanın ve yüklemenin çok zahmetli olacağını düşündüm ve bunu elle yazdım:

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

Geriye dönüp bakıldığında, modüller bu şeylerin neredeyse tamamını konvansiyonla tanımladığından IoC çerçevesini kullanmak daha hızlı olurdu.

Bu sorunun cevaplarını ve yorumlarını incelemek için biraz zaman harcadıktan sonra, bir IoC kabı kullanmaya karşı çıkan insanların gerçek bağımlılık enjeksiyonu uygulamadığına inanıyorum. Gördüğüm örnekler, bağımlılık enjeksiyonuyla yaygın olarak karıştırılan uygulamalardır. Bazı insanlar kodu “okuma” güçlüğünden şikayet ediyorlar. Doğru şekilde yapılırsa, DI'yi elle kullanırken IoC kabı kullanırken olduğu gibi kodunuzun büyük çoğunluğu aynı olmalıdır. Aradaki fark tamamen uygulama içindeki birkaç "fırlatma noktasında" yer almalıdır.

Başka bir deyişle, IoC kaplarını sevmiyorsanız, muhtemelen yapılması gerektiği gibi Bağımlılık Enjeksiyonu yapmıyorsunuzdur.

Başka bir nokta: Herhangi bir yerde yansıma kullanırsanız, Bağımlılık Enjeksiyonu gerçekten elle yapılamaz. Gezinmeyi kodlamak için ne düşündüğünden nefret ederken, gerçekten önlenemeyeceği belirli alanlar olduğunu bilmelisiniz. ASP.NET MVC, örneğin, her istek üzerine yansıma yoluyla denetleyiciyi başlatmaya çalışır. Elle bağımlılık enjeksiyonu yapmak için, her denetleyiciyi bir "bağlam kökü" haline getirmeniz gerekir :

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

Şimdi bunu bir DI çerçevesinin sizin için yapmasına izin vererek karşılaştırın:

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

Bir DI çerçevesi kullanarak aşağıdakilere dikkat edin:

  • Bu sınıfı birim test edebilirim. Bir alay oluşturarak ISimpleWorkflowInstanceMerger, bir veritabanı bağlantısına veya başka bir şeye ihtiyaç duymadan tahmin ettiğim şekilde kullanıldığını test edebilirim.
  • Çok daha az kod kullanıyorum ve kodu okumak çok daha kolay.
  • Bağımlılığımın bağımlılığından biri değişirse, denetleyicide herhangi bir değişiklik yapmak zorunda değilim. Bu, özellikle birden fazla denetleyicinin aynı bağımlılıklardan bazılarını kullanacağını düşündüğünüzde iyidir.
  • Ben asla benim veri katmanından sınıfları referans. Web uygulamam sadece ISimpleWorkflowInstanceMergerarayüzü içeren projeye bir referans içerebilir . Bu, uygulamayı ayrı modüllere ayırmamı ve gerçek bir çok katmanlı mimariyi korumamı sağlar, bu da işleri daha esnek hale getirir.

Tipik bir web uygulamasında birkaç denetleyici bulunur. Her denetleyicide DI'yi elle yapmanın tüm acıları, uygulamanız büyüdükçe gerçekten toplanacaktır. Tek bir bağlam köküne sahip bir uygulamanız varsa, hiçbir zaman yansımayla bir hizmeti başlatmaya çalışmazsanız, bu büyük bir sorun değildir. Bununla birlikte, bağımlılık grafiğini yönetmek için bir tür çerçeve kullanmazsanız, Bağımlılık Enjeksiyonu kullanan herhangi bir uygulamanın yönetimi belirli bir boyuta ulaştığında son derece pahalı hale gelecektir.


9

"Yeni" anahtar kelimeyi her kullandığınızda, somut bir sınıf bağımlılığı yaratırsınız ve kafanızda küçük bir alarm zili çalmalıdır. Bu nesneyi ayrı ayrı test etmek zorlaşır. Çözüm, arabirimlere programlamak ve bağımlılığı enjekte etmektir, böylece nesne, bu arabirimi uygulayan herhangi bir şeyle (örneğin, alaylar) birim test edilebilir.

Sorun şu ki, bir yerde nesneler inşa etmek zorundasınız. Fabrika düzeni, kuplajı POXO'larınızdan çıkarmanın bir yoludur (Düz Eski "OO dilinizi buraya ekleyin" Nesneler). Siz ve iş arkadaşlarınız bu şekilde kod yazıyorsanız, IoC kapsayıcısı kod tabanınıza yapabileceğiniz bir sonraki "Artan İyileştirme" dir. Tüm bu kötü Fabrika demirbaş kodunu temiz nesnelerinizden ve iş mantığınızdan çıkarır. Anlayacaklar ve çok sevecekler. Heck, bir şirkete neden onu sevdiğini konuş ve herkesi coştur.

İş arkadaşlarınız henüz DI yapmıyorsa, önce buna odaklanmanızı öneririm. Kolayca test edilebilen temiz kodun nasıl yazıldığına dair sözcüğü yayın. Temiz DI kodu zor kısımdır, orada olduğunuzda, nesne kablolama mantığını Fabrika sınıflarından bir IoC konteynerine kaydırmak nispeten önemsiz olmalıdır.


8

Tüm bağımlılıklar açıkça görülebildiğinden, gevşek bir şekilde bağlanan ve aynı zamanda uygulama genelinde kolayca erişilebilir ve tekrar kullanılabilen bileşenler oluşturmayı teşvik eder.


7

Sen yapmıyorsun IoC konteynerine ihtiyacınız .

Ancak bir DI modelini titizlikle takip ediyorsanız, bir tanesine sahip olmanın bir ton gereksiz, sıkıcı kodu kaldıracağını göreceksiniz.

Bu genellikle bir kütüphane / çerçeve kullanmak için en iyi zamandır - ne yaptığını anladığınızda ve bunu kütüphane olmadan yapabileceğiniz zaman.


6

Ben sadece evde yetiştirilen DI kodu dışarı çekilmesi ve bir IOC ile değiştirme sürecinde olmak olur. Muhtemelen 200'den fazla kod satırını kaldırdım ve yaklaşık 10 ile değiştirdim. Evet, konteynırı (Winsor) nasıl kullanacağımı öğrenmek zorunda kaldım, ancak internet teknolojilerinde çalışan bir mühendisim. 21. yüzyılda buna alışkınım. Muhtemelen nasıl tos bakarak yaklaşık 20 dakika geçirdim. Bu benim zamanım oldu.


3
"ama 21. yüzyılda internet teknolojileri üzerinde çalışan bir mühendisim, bu yüzden buna alışkınım" -> +1
user96403

5

Sınıflarınızı ayırmaya ve bağımlılıklarınızı tersine çevirmeye devam ettikçe, sınıflar küçük kalmaya devam eder ve "bağımlılık grafiği" büyümeye devam eder. (Bu kötü değil.) Bir IoC konteynerinin temel özelliklerini kullanmak, tüm bu nesneleri kablolamayı önemsiz hale getirir, ancak manuel olarak yapmak çok külfetli olabilir. Örneğin, yeni bir "Foo" örneği oluşturmak istiyorsam, ancak "Bar" a ihtiyaç duyarsa. Ve bir "Bar" ın "A", "B" ve "C" ye ihtiyacı vardır. Ve bunların her birinin başka 3 şeye vb. İhtiyacı var. (Evet, iyi sahte isimler bulamıyorum :)).

Nesne grafiğinizi sizin için oluşturmak için bir IoC kapsayıcısı kullanmak bir ton karmaşıklığı azaltır ve tek seferlik yapılandırmaya iter. Ben sadece "bana bir 'Foo' yaratın" diyorum ve bir tane inşa etmek için neyin gerekli olduğunu anlıyor.

Bazı insanlar IoC kaplarını çok daha fazla altyapı için kullanırlar, bu da gelişmiş senaryolar için iyidir, ancak bu durumlarda, yeni geliştiricilerin kodunu okumayı ve hata ayıklamayı zorlaştırabileceğini kabul ediyorum.


1
neden onları çekmek için çalışmak yerine en düşük ortak paydayı yatıştırmaya devam ediyoruz?
stevenharman

4
Başvurmakla ilgili bir şey söylemedim. Az önce gelişmiş senaryoların yeni geliştiriciler için işleri daha da gizleyebileceğini söyledim - bu bir gerçek. "Öyle yapma" demedim. Ancak o zaman bile (şeytanın savunucusu olma zamanı), kod tabanını daha açık ve ulaşılabilir hale getiren aynı şeyi başarmanın başka yolları da vardır. Altyapı aracı özel yapılandırmanızdaki karmaşıklığı gizlemek, sadece yapabileceğiniz için doğru neden değildir ve kimseyi yukarı çekmez.
Aaron Lerch

4

Birlik Hakkında Dittos. Çok büyüyün ve kirişlerdeki çıtırtıları duyabilirsiniz.

İnsanlar, IoC kodunun ne kadar temiz göründüğü konusunda bir zamanlar C ++'daki şablonların 90'larda geri dönmenin zarif bir yolu olduğu hakkında konuştular, ancak günümüzde bunları gizemli olarak çözecekleri konusunda beni şaşırtmaya başladığında beni şaşırtmıyor. . Bah!


2

.NET dünyasında AOP çok popüler değildir, bu nedenle DI için bir çerçeve, ister kendiniz yazın ister başka bir çerçeve kullanın, tek gerçek seçeneğinizdir.

AOP kullandıysanız, Java'da daha yaygın olan uygulamanızı derlerken enjekte edebilirsiniz.

Birim testinin daha kolay olması için düşük kuplaj gibi DI'nin birçok faydası vardır, ancak bunu nasıl uygulayacaksınız? Yansımayı kendiniz yapmak için kullanmak ister misiniz?


Yeni MEF çerçevesi sadece bu .NET için izin verir ve kodun daha temiz ve anlaşılması daha kolay olduğunu söylemeliyim, DI kavramını anlamak çok daha kolay hale getirir.
terjetyl

4
@TT: MEF değil bir bağımlılık enjeksiyon konteyner ve AOP ile ilgisi yoktur.
Mauricio Scheffer

2

Neredeyse 3 yıl geçti, değil mi?

  1. IoC çerçeveleri lehine yorum yapanların% 50'si IoC ve IoC çerçeveleri arasındaki farkı anlamıyor. Bir uygulama sunucusuna dağıtılmadan kod yazabileceğinizi bildiklerinden şüpheliyim

  2. En popüler Java Spring çerçevesini alırsak, XMl'den koda taşınan IoC yapılandırmasıdır ve şimdi şöyle görünmektedir

`@Configuration genel sınıf AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

} `` Bunu tam olarak yapabilmek için bir çerçeveye ihtiyacımız var.


1

Dürüst olmak gerekirse, IoC konteynırlarına ihtiyaç duyulan birçok durum bulamıyorum ve çoğu zaman sadece gereksiz karmaşıklık ekliyorlar.

Eğer onu sadece bir nesnenin yapımını kolaylaştırmak için kullanıyorsanız, sormak zorundayım, bu nesneyi birden fazla yerde mi başlatıyorsunuz? Singleton ihtiyaçlarınızı karşılamaz mı? Yapılandırmayı çalışma zamanında değiştiriyor musunuz? (Veri kaynağı türlerini değiştirme, vb.).

Evetse, bir IoC kapsayıcısına ihtiyacınız olabilir. Değilse, başlatmayı geliştiricinin kolayca görebileceği yerden uzaklaştırıyorsunuz.

Bir arayüzün mirastan daha iyi olduğunu kim söyledi? Bir Hizmeti test ettiğinizi varsayalım. Neden DI yapıcısını kullanmıyorsunuz ve kalıtım kullanarak bağımlılıklardan alaylar oluşturmuyorsunuz? Kullandığım çoğu hizmetin sadece birkaç bağımlılığı var. Ünite testini bu şekilde yapmak, tonlarca işe yaramaz arabirimin korunmasını önler ve sahip olmadığınız anlamına gelir hızla Resharper kullanıyoruz bir yöntemin beyanı bulmak için.

Çoğu uygulama için, IoC Konteynerlerinin gereksiz kodu kaldırdığını söyleyen bir efsane olduğuna inanıyorum.

İlk olarak, konteyneri ilk etapta kuruyoruz. Daha sonra, başlatılması gereken her nesneyi tanımlamanız gerekir. Başlatma sırasında kodu kaydetmezsiniz, taşıyabilirsiniz (nesneniz birden fazla kullanılmadıkça. Tekli olarak daha iyi mi?). Ardından, bu şekilde başlattığınız her nesne için bir arayüz oluşturmanız ve bakımını yapmanız gerekir.

Bu konuda herhangi bir fikri olan var mı?


Küçük projeler için bile, yapılandırmayı XML'ye yerleştirmek çok daha temiz ve modüler hale getirir - ve ilk kez, kodu tekrar kullanılabilir hale getirir (artık yapıştırıcı ile kirlenmediğinden). Uygun yazılım ürünleri için, yalnızca veya değiştirilmiş bir XML dosyasını dağıtarak, örneğin ShippingCostCalculatorveya tam olarak aynı kod tabanındakiProductUIPresentation herhangi bir stratejiyi / uygulamayı yapılandırabilir veya değiştirebilirsiniz .
Thomas W

Genel bir uygulama olarak, daha az değil, daha fazla karmaşıklık eklediğinizi düşünüyorum. Çalışma zamanında bir şeyi değiştirmeniz gerektiğinde, harikalar, ama neden şaşkınlığı ekliyorsunuz? Asla kapatılmayacak şeyler için neden ekstra arayüzlerin yükünü koruyorsunuz? Test için IoC kullanma demiyorum. Elbette, özellik veya hatta yapıcı bağımlılığı enjeksiyonu kullanın. Bir arayüz kullanmanız mı gerekiyor? Test nesneleriniz neden alt sınıfta olmasın? Bu daha temiz olmaz mıydı?
Fred

XML yapılandırması / IoC yapmak için arabirimlere ihtiyacınız yoktur. Sadece ~ 8 sayfa ve 5 veya 6 hizmet ile küçük bir projede netlikte büyük gelişmeler buldum. Orada daha az gizleme hizmetleri ve denetleyicileri artık gerek tutkal kodu beri.
Thomas W

1

Bağımlılıklarınızın yapılandırmasını merkezileştirmeniz gerekiyorsa, yığın halinde kolayca değiştirilebilmeleri için bir IoC kapsayıcısına ihtiyacınız olacaktır. Bu, birçok bağımlılığın yer değiştirdiği ancak bağımlılıklar arasında çok az bağımlılığın olduğu TDD'de en mantıklıdır. Bu, nesne yapımının kontrol akışını bir dereceye kadar gizleme pahasına yapılır, bu nedenle iyi organize edilmiş ve makul bir şekilde belgelenmiş bir konfigürasyona sahip olmak önemlidir. Bunu yapmak için bir nedene sahip olmak da iyidir, aksi takdirde sadece soyutlama altın kaplamadır . O kadar kötü yapıldığını gördüm ki, inşaatçılar için bir goto ifadesine eşdeğer olmaya sürüklendi.


1

İşte nedeni. Projeye Ninject-Ninject adı verilir. Visual Studio ile indirip çalıştırabilirsiniz. Bu örnek Ninject kullanır, ancak TÜM 'yeni' ifadeleri tek bir konumdadır ve hangi bağlama modülünü kullanacağınızı değiştirerek uygulamanızın çalışma şeklini tamamen değiştirebilirsiniz. Örnek, hizmetlerin alaycı bir sürümüne veya gerçek sürüme bağlanabilmeniz için ayarlanır. Önemli olmayan küçük projelerde, ancak büyük projelerde büyük bir sorun.

Açık olmak gerekirse, onları gördüğüm gibi avantajlar: 1) Kod kökündeki bir konumda TÜM yeni ifadeler. 2) Tek bir değişiklikle tamamen yeniden faktör kodu. 3) 'Serin faktör' için ekstra puan çünkü ... iyi: güzel. : p


1

IOC'nin neden benim açımdan iyi olmayabileceğini bulmaya çalışacağım.

Diğer her şeyde olduğu gibi, IOC kapsayıcısı (veya Einstein'ın söylediği gibi I = OC ^ 2), kodunuzda ihtiyacınız olup olmadığına kendiniz karar vermeniz gereken bir kavramdır. IOC hakkındaki son moda çığlığı sadece modadır. Modaya düşmeyin, ilk önce. Kodunuzda uygulayabileceğiniz sayısız kavram var. Her şeyden önce, programlamaya başladığımdan beri bağımlılık enjeksiyonunu kullanıyorum ve bu isim altında popülerleştirildiğinde terimin kendisini öğrendim. Bağımlılık kontrolü çok eski bir konudur ve neyin ayrıldığına bağlı olarak şimdiye kadar trilyonlarca yolla ele alınmıştır. Her şeyi her şeyden ayırmak saçmalıktır. IOC kapsayıcısındaki sorun, Entity Framework veya NHibernate kadar faydalı olmaya çalışmasıdır. Herhangi bir veritabanını sisteminizle birleştirmeniz gerektiğinde bir nesne-ilişkisel eşleyici yazmak bir zorunluluk olsa da, IOC kapsayıcısı her zaman gerekli değildir. Yani IOC kabı yararlı olduğunda:

  1. Düzenlemek istediğiniz birçok bağımlılığa sahip bir durum olduğunda
  2. Kodunuzu üçüncü taraf ürünüyle eşleştirmeyle ilgilenmediğinizde
  3. Geliştiricileriniz yeni bir araçla nasıl çalışılacağını öğrenmek istediğinde

1: Kodunuzda çok fazla bağımlılığa sahip olduğunuz ya da tasarımın erken aşamalarında farkında olduğunuz kadar sık ​​değildir. Soyut düşünme, soyut düşünme zamanı geldiğinde yararlıdır.

2: Kodunuzu üçüncü taraf koduyla eşleştirmek HuGe problemidir. Ben 10 + yaşında ve o zaman fantezi ve gelişmiş kavramlar ATL, COM, COM + vb izleyen kod ile çalışıyordu. Artık bu kodla yapabileceğiniz hiçbir şey yok. Söylediğim şey, gelişmiş bir kavramın belirgin bir avantaj sağladığı, ancak bu uzun vadede eski avantajın kendisi ile iptal edildi. Sadece hepsini daha pahalı yapmıştı.

3: Yazılım geliştirme yeterince zor. Bazı gelişmiş kavramların kodunuzda kırpılmasına izin verirseniz, tanınmayan düzeylere genişletebilirsiniz. IOC2 ile ilgili bir sorun var. Bağımlılıkları ayırmakla birlikte, mantık akışını da ayırmaktadır. Bir hata bulduğunuzu ve durumu incelemek için bir mola vermeniz gerektiğini düşünün. IOC2, diğer gelişmiş kavramlar gibi, bunu daha da zorlaştırmaktadır. Bir kavramın içindeki bir hatayı düzeltmek, bir plainer kodundaki bir hatayı düzeltmekten daha zordur, çünkü bir hatayı düzelttiğinizde bir kavrama tekrar uyulması gerekir. (Sadece bir örnek vermek gerekirse, C ++ .NET sözdizimini o kadar çok değiştiriyor ki, .NET'in bazı eski sürümlerini yeniden düzenlemeden önce çok düşünmeniz gerekiyor.) Peki IOC ile ilgili sorun nedir? Sorun bağımlılıkları çözmektir. Çözme mantığı genellikle IOC2'nin kendisinde gizlidir, belki öğrenmeniz ve sürdürmeniz gereken nadir bir şekilde yazılmış. Üçüncü taraf ürününüz 5 yıl içinde orada olacak mı? Microsoft değildi.

"Nasıl biliyoruz" sendromu IOC2 ile ilgili her yere yazılmıştır. Bu, otomasyon testine benzer. İlk bakışta şık terim ve mükemmel çözüm, tüm testlerinizi gece boyunca yürütmek ve sabahları sonuçları görmek için basitçe koyabilirsiniz. Şirketten sonra otomatik testin gerçekten ne anlama geldiğini açıklamak gerçekten acı vericidir. Otomatik test, ürününüzün kalitesini artırmak için gece boyunca uygulayabileceğiniz hata sayısını azaltmanın kesinlikle hızlı bir yolu değildir. Ancak, moda bu kavramı sinir bozucu bir şekilde baskın hale getiriyor. IOC2 aynı sendroma sahiptir. Yazılımınızın iyi olması için onu uygulamanız gerektiğine inanılmaktadır. Her geçen gün yapılan görüşmede IOC2 ve otomasyon uygulayıp uygulamadığım soruldu. Bu bir moda işareti: şirketin MFC'de yazılan kodun bir kısmı terk etmeyecekti.

IOC2'yi yazılımdaki diğer kavramlar olarak öğrenmeniz gerekir. IOC2'nin kullanılması gerektiğine karar, ekip içinde ve şirkette verilir. Ancak, karar verilmeden önce en azından TÜM argümanlardan bahsedilmelidir. Sadece artı tarafın negatif tarafa ağır bastığını görürseniz, olumlu bir karar verebilirsiniz.

IOC2'de sadece çözdüğü sorunları çözmesi ve getirdiği sorunları ortaya çıkarması dışında yanlış bir şey yoktur. Başka hiçbir şey. Ancak, modaya karşı çıkmak çok zor, ter ağzı, her şeyin takipçisi var. Fantezileriyle ilgili sorun ortaya çıktığında hiçbirinin orada olmaması gariptir. Yazılım endüstrisindeki birçok kavram, kâr yarattığı, kitaplar yazıldığı, konferanslar düzenlendiği, yeni ürünler yapıldığı için savunulmuştur. Bu moda, genellikle kısa ömürlü. İnsanlar başka bir şey bulur bulmaz onu tamamen terk ederler. IOC2 kullanışlıdır, ancak gördüğüm diğer birçok kavramla aynı işaretleri gösterir. Hayatta kalacak mı bilmiyorum. Bunun için bir kural yok. Eğer faydalı olursa, hayatta kalacağını düşünüyorsunuz. Hayır, bu şekilde gitmiyor. Büyük bir zengin şirket yeterlidir ve konsept birkaç hafta içinde ölebilir. Göreceğiz. NHibernate hayatta kaldı, EF ikinci oldu. Belki IOC2 de hayatta kalacaktır. Yazılım geliştirmedeki çoğu kavramın özel bir şey olmadığını, çok mantıklı, basit ve açık olduğunu ve bazen mevcut adlandırma kuralını hatırlamak kavramın kendisini anlamaktan daha zordur. IOC2 bilgisi bir geliştiriciyi daha iyi bir geliştirici yapar mı? Hayır, çünkü bir geliştirici doğada IOC2'ye benzer bir kavram bulamadıysa, IOC2'nin hangi sorunu çözdüğünü anlaması zor olacak, onu kullanmak yapay görünecek ve kullanmaya başlayabilir politik olarak doğru olmak için. Yazılım geliştirmedeki çoğu kavramın özel bir şey olmadığını, çok mantıklı, basit ve açık olduğunu ve bazen mevcut adlandırma kuralını hatırlamak kavramın kendisini anlamaktan daha zordur. IOC2 bilgisi bir geliştiriciyi daha iyi bir geliştirici yapar mı? Hayır, çünkü bir geliştirici doğada IOC2'ye benzer bir kavram bulamadıysa, IOC2'nin hangi sorunu çözdüğünü anlaması zor olacak, onu kullanmak yapay görünecek ve kullanmaya başlayabilir politik olarak doğru olmak için. Yazılım geliştirmedeki çoğu kavramın özel bir şey olmadığını, çok mantıklı, basit ve açık olduğunu ve bazen mevcut adlandırma kuralını hatırlamak kavramın kendisini anlamaktan daha zordur. IOC2 bilgisi bir geliştiriciyi daha iyi bir geliştirici yapar mı? Hayır, çünkü bir geliştirici doğada IOC2'ye benzer bir kavram bulamadıysa, IOC2'nin hangi sorunu çözdüğünü anlaması zor olacak, onu kullanmak yapay görünecek ve kullanmaya başlayabilir politik olarak doğru olmak için. IOC2 bilgisi bir geliştiriciyi daha iyi bir geliştirici yapar mı? Hayır, çünkü bir geliştirici doğada IOC2'ye benzer bir kavram bulamadıysa, IOC2'nin hangi sorunu çözdüğünü anlaması zor olacak, onu kullanmak yapay görünecek ve kullanmaya başlayabilir politik olarak doğru olmak için. IOC2 bilgisi bir geliştiriciyi daha iyi bir geliştirici yapar mı? Hayır, çünkü bir geliştirici doğada IOC2'ye benzer bir kavram bulamadıysa, IOC2'nin hangi sorunu çözdüğünü anlaması zor olacak, onu kullanmak yapay görünecek ve kullanmaya başlayabilir politik olarak doğru olmak için.


0

Şahsen, IoC'yi uygulamamın bir tür yapı haritası olarak kullanıyorum (Evet, StructureMap'ı da tercih ediyorum;)). Testler sırasında genel arayüz uygulamalarımı Moq uygulamaları ile değiştirmeyi kolaylaştırır. Bir test kurulumu oluşturmak, IoC çerçevem ​​için yeni bir init-call yapmak kadar kolay olabilir, hangi sınıfın benim test sınırım olduğunu bir taklitle değiştiririm.

Muhtemelen IoC bunun için değil, ama kendimi en çok kullandığım şey bu.




0

Bunun oldukça eski bir yazı olduğunun farkındayım, ama yine de oldukça aktif görünüyor ve diğer cevaplarda henüz belirtilmemiş birkaç noktaya katkıda bulunacağımı düşündüm.

Bağımlılık enjeksiyonunun faydalarına katıldığımı söyleyeceğim, ancak cevabında Maxm007 tarafından özetlenmeyen bir desen kullanarak nesneleri kendim oluşturmayı ve yönetmeyi tercih ediyorum. Üçüncü taraf kapsayıcılarını kullanmayla ilgili iki ana sorun buldum:

1) Bir üçüncü taraf kütüphanesinin nesnelerinizin ömrünü "otomatik olarak" yönetmesi beklenmedik sonuçlara yol açabilir. Özellikle büyük projelerde, bir nesnenin beklediğinizden çok daha fazla kopyaya sahip olabileceğinizi ve yaşam döngülerini manuel olarak yönettiğinizden daha fazla kopyaya sahip olabileceğinizi bulduk. Eminim bu kullanılan çerçeveye bağlı olarak değişir, ancak yine de sorun var. Nesneniz bazen kaynaklar beklediğinizden daha uzun süre yaşayabileceğinden, nesnenizde kaynaklar, veri bağlantıları vb. Varsa bu sorunlu olabilir. Bu nedenle, kaçınılmaz olarak, IoC kapları bir uygulamanın kaynak kullanımını ve bellek ayak izini artırma eğilimindedir.

2) IoC kapları, bence "kara kutu programlama" biçimidir. Özellikle, daha az deneyimli geliştiricilerimizin onları kötüye kullanma eğiliminde olduğunu gördüm. Programlayıcının nesnelerin birbirleriyle nasıl ilişki kurması gerektiği veya nasıl ayrıştırılması gerektiğini düşünmesine izin vermez, çünkü onlara istedikleri herhangi bir nesneyi ince havadan kolayca alabilecekleri bir mekanizma sağlar. Örneğin, ObjectA'nın hiçbir zaman doğrudan ObjectB hakkında bilmemesi için iyi bir tasarım nedeni olabilir, ancak bir fabrika veya köprü veya servis bulucu oluşturmak yerine, deneyimsiz bir programcı sadece "sorun değil, ObjectB'yi IoC konteynerinden alacağım ". Bu aslında IoC'nin önlemeye yardımcı olması gereken nesne bağlantısının artmasına neden olabilir.


-8

ASP.NET projesinde Bağımlılık Enjeksiyonu birkaç kod satırı ile gerçekleştirilebilir. Birden fazla ön uç kullanan ve birim testlere ihtiyaç duyan bir uygulamanız olduğunda bir kap kullanmanın bazı avantajları olduğunu düşünüyorum.


1
bir örnek verebilir misin? ASP.NET'te DI'nin diğer uygulama türlerine göre farkı nedir?
tofi9
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.