2013-04-16 33 views
26

Şu anda Dagger'u kullanmak üzere bir Android uygulamasının yeniden tasarlanmasını yapıyorum. Uygulamam büyük ve karmaşık ve yakın zamanda şu senaryoyu karşılıyorum:Yapıcılara bağımlılık enjeksiyonu için Hançeri Kullanılıyor

Nesne A, enjeksiyon için mükemmel bir aday olan özel bir DebugLogger örneği gerektirir. Kaydediciyi geçmektense, sadece A'nın yapıcısı ile enjekte edebilirim. Bu şu gibi bir şeye benziyor: Şimdilik bu mantıklıdır. Bununla birlikte, A böylece şeyler yapmanın Dagger yolunu izleyerek A birden çok örneği inşa edilmelidir başka sınıf B. tarafından inşa edilmesi gerekir Basit B içine Provider<A> enjekte:

class B 
{ 
    private Provider<A> aFactory; 

    @Inject 
    public B(Provider<A> aFactory) 
    { 
     this.aFactory = aFactory; 
    } 
} 

Tamam, iyi şimdiye kadar. Ama bekle, birdenbire inşası için hayati olan "miktar" olarak adlandırılan bir tamsayı gibi ek girişlere ihtiyaç var. Şimdi, A için benim kurucu böyle bakmak gerekiyor:

@Inject 
public A(DebugLogger logger, int amount) 
{ 
... 
} 

Aniden bu yeni parametre enjeksiyon engeller. Üstelik, bu işe yaramış olsa bile, yanıltıcı olmadıkça, sağlayıcıdan yeni bir örnek alınırken "miktar" da geçmem için bir yol olmazdı. Burada yapabileceğim birkaç şey var ve sorum şu ki hangisi en iyisi?

Kurucudan sonra çağrılması beklenen setAmount() yöntemini ekleyerek A'yı yeniden ayarlayabilirim. Bu çirkin bir durumdur, çünkü bu, "miktar" ın doldurulmasına kadar A'nın inşasını geciktirmeye zorlar. Eğer iki tür parametreye, "miktar" ve "frekans" a sahip olsaydım, o zaman iki ayarlayıcıya sahip olurdum. karmaşık hem belirleyiciler olarak adlandırılır devam eder sonra A'nın o inşaat sağlamak için kontrol, yoksa şöyle karışımı içine henüz üçüncü yöntemi eklemek gerekir:

(Somewhere in B): 

A inst = aFactory.get(); 
inst.setAmount(5); 
inst.setFrequency(7); 
inst.doConstructionThatRequiresAmountAndFrequency(); 

diğer alternatif Ben yapıcı kullanmak kalmamasıdır tabanlı enjeksiyon ve alan bazlı enjeksiyon ile gidin. Ama şimdi, alanlarımı herkese açık hale getirmeliyim. Bu benimle iyi oturmuyor, çünkü şimdi sınıflarımın iç verilerini diğer sınıflara gösterme yükümlülüğüm var.

Şimdiye kadar, aklıma gelen tek biraz zarif çözüm şöyle, sağlayıcıları için alan bazlı enjeksiyon kullanmaktır:

class A 
{ 
    @Inject 
    public Provider<DebugLogger> loggerProvider; 
    private DebugLogger logger; 

    public A(int amount, int frequency) 
    { 
     logger = loggerProvider.get(); 
     // Do fancy things with amount and frequency here 
     ... 
    } 
} 

bile hala, ben beri, zamanlama konusunda emin değilim Marancı firmamın çağrılmasından önce sağlayıcıyı enjekte edeceğinden emin değilim.

Daha iyi bir yolu var mı? Hançerin nasıl çalıştığı hakkında bir şey mi duyuyorum?

cevap

49

Konuştuğunuz şey yardımlı enjeksiyon olarak bilinir ve şu anda Dagger tarafından otomatik olarak desteklenmez.

fabrika desenli bu çalışabilirsiniz: Bir oluşturmak için gereken zaman

class B { 
    @Inject AFactory aFactory; 

    //... 
} 

ve:

class AFactory { 
    @Inject DebugLogger debuggLogger; 

    public A create(int amount, int frequency) { 
    return new A(debuggLogger, amount); 
    } 
} 

Şimdi bu fabrikayı enjekte ve A örneklerini oluşturmak için kullanabilirsiniz A 'miktarınız' ve 'frekansınız' ile birlikte fabrikayı kullanırsınız.A yine kayıt cihazı örneğini temin etmek üzere enjeksiyon kullanarak kayıt cihazı, tutar ve frekanslı alanların final örneğini için

A a = aFactory.create(amount, frequency); 

Bu sağlar.

Guice, bu fabrikaların sizin için oluşturulmasını esasen otomatikleştiren yardımlı bir enjeksiyon eklentisine sahiptir. Hançer posta listesine have been discussion adresinden eklenecek uygun bir yol hakkında bilgi verilmesine rağmen, bu yazı ile ilgili karar verilmemiştir.

+0

Hızlı yanıtınız için teşekkür ederiz. Tanımladığınız fabrika modeli en iyi yaklaşım gibi görünüyor. Ayrıca Dagger özel alan enjeksiyonunu desteklediyse, gerçekleştirmeye çalıştığım şeye kolayca izin vereceğini de fark ettim. Bunun neden Dagger'ın orijinal tasarımının bir parçası olmadığını merak ediyorum? Dagger'ın yansımayı kullanarak enjekte ettiğini farz ediyorum. Öyleyse, özel alanlar sorun yaratmaz, değil mi? – Alex

+0

Hançer yansımasına geri döner, ancak bu birincil enjeksiyon aracı değildir. Doğrudan alanları belirleyen ya da kurucularınızı arayarak kod üretir. Bu şekilde, kaynak ağacınızdaki herhangi bir kod parçası gibi çalışır ve "özel" üyelere erişemez. –

+0

Ve bazı güvenlik yöneticileri, sınıflarınızdaki erişilebilirliği değiştirmeye dayanan yansımayı kıracak. –

3

Jake'in yazısının söylediği şey doğrudur. Biz (biz Guice ve Hançer ile çalışan bazı Google halkı), biz "yardımlı enjeksiyon" veya Guice veya Hançer veya tek başına kullanılabilir olması gereken otomatik fabrika üretimi alternatif bir sürümü üzerinde çalışıyoruz - yani, dedi sizin için fabrika sınıfı kaynak kodu üretecektir. Bu fabrika sınıfları (eğer uygunsa) herhangi bir standart JSR-330 sınıfı gibi enjekte edilebilir olacaktır. Ama henüz serbest değil.

Böyle bir çözüm beklemede, Jake Wharton'un yaklaşımı tavsiye edilir.

+0

Evet, endişelenmeyin, hani Dagger'in hala ilk versiyonlarinda oldugunu farkettim. Ben bu diğer özelliklerin bazıları, ikili sistemin genel boyutunu korumak için eklentiler olarak geliştirildiğini (örneğin, en azından benim için Android geliştirme için bu kadar çekici bir seçenek kılan) sevdim. – Alex

+1

Bu nedenle, otomatik fabrika üretiminden bahsedildi: https://github.com/google/auto/tree/master/factory – phazei

3

Oluşturucunuzda enjektabl ve enjekte edilemeyenleri karıştırdığınız için bir sorun yaşıyorsunuz. Size gönül yarası ton kaydetmek ve temiz kodunuzu tutacak enjeksiyon için genel kurallar şunlardır:

  1. Enjektabllar onların yapıcı diğer enjektabl istemek değil newables için olabilir.

  2. Devirler, enjekte edilebilirler için değil, kurucularındaki diğer yeni malzemeleri isteyebilir.

Enjektabllar hizmet türü nesneler, yani vb bir CreditCardProcessor, musicplayer gibi çalışıyor mu nesnelerdir değer türü vb

0

Jake'in yazı CREDITCARD, Song gibi nesneler vardır

Newables harika, ama daha basit bir yol var. Google, fabrikada derleme zamanında otomatik olarak oluşturmak için AutoFactory kütüphanesini oluşturdu.

Birincisi, argümanlar enjekte için @AutoFactory açıklama ve @Provided ek açıklama ile sınıf A oluşturun:

@AutoFactory 
public class A { 

    private DebugLogger logger; 

    public A(@Provided DebugLogger logger, int amount, int frequency) { 
     this.logger = logger; 
    } 
} 

Ardından kütüphane derleme sırasında AFactory sınıfını oluşturur. Yani fabrikaya B sınıfından bir kurucuyu enjekte etmeniz yeterlidir.

public class B { 

    private final AFactory aFactory; 

    @Inject 
    public B(AFactory aFactory) { 
     this.aFactory = aFactory; 
    } 

    public A createA(int amount, int frequency) { 
     return aFactory.create(amount, frequency); 
    } 
} 
İlgili konular