2013-04-16 7 views
26

그래서 현재 Dagger을 사용하기 위해 Android 앱을 다시 디자인하고 있습니다. 내 응용 프로그램은 크고 복잡하며 최근에 다음 시나리오를 보았습니다.생성자 의존성 주입에 대거 사용

Object A는 완벽한 인젝션 후보 인 특수 DebugLogger 인스턴스가 필요합니다. 로그 작성기를 돌아 다니는 대신 A의 생성자를 통해 주입 할 수 있습니다. 이 모양은 다음과 같습니다.

class A 
{ 
    private DebugLogger logger; 

    @Inject 
    public A(DebugLogger logger) 
    { 
     this.logger = logger; 
    } 

    // Additional methods of A follow, etc. 
} 

지금까지는 이것이 합리적입니다. 그러나 A는 그렇게 일을 단검의 방법에 따라, (A)의 여러 인스턴스를 구축 할 수 있어야 다른 클래스 B로 구축 할 필요가있다, 나는 간단한 B에 Provider<A>를 주입 :

class B 
{ 
    private Provider<A> aFactory; 

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

좋아, 좋은 지금까지. 그러나 잠깐, 갑자기 A는 그 구성에 필수적인 "양"이라는 정수와 같은 추가 입력이 필요합니다. 이제 A에 대한 내 생성자는 다음과 같이 표시되어야합니다.

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

갑자기이 새 매개 변수가 주입을 방해합니다. 더욱이 이것이 작동했다하더라도 내가 잘못하지 않는 한 제공자로부터 새로운 인스턴스를 검색 할 때 "양"을 전달할 수있는 방법이 없습니다. 내가 여기서 할 수있는 일이 몇 가지 있는데, 나의 질문은 어느 것이 최고인가?

생성자 다음에 호출 될 것으로 예상되는 setAmount() 메서드를 추가하여 A를 리팩토링 할 수 있습니다. 그러나 이것은 "양"이 채워질 때까지 A의 생성을 지연 시키므로 추악합니다. "amount"와 "frequency"라는 두 개의 매개 변수가 있다면 두 개의 setter가 생겨 복잡한 두 세터가 호출 재개 후의 구성을 확인하려면, 또는 그런 것처럼, 혼합으로 아직 세 번째 방법을 추가해야합니다 :

(Somewhere in B): 

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

다른 대안은 내가 생성자를 사용하지 않는 것이있다 기반 주입 및 필드 기반 주입으로 이동합니다. 하지만 지금은 밭을 공개해야합니다. 내 수업의 내부 데이터를 다른 수업에 공개해야 할 의무가 있기 때문에 이것은 나에게 잘 맞지 않습니다.

지금까지 내가 생각할 수있는 유일한 다소 우아한 솔루션과 같이, 공급자에 대한 필드 기반의 주사를 사용하는 것입니다

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

에도 여전히 나는 '이후이 타이밍에 대해 확실 해요 Dagger가 생성자가 호출되기 전에 공급자를 삽입 할 것인지 확실하지 않습니다.

더 좋은 방법이 있습니까? Dagger가 작동하는 방식에 대해 뭔가 빠져 있습니까?

답변

49

당신이 말하는 것은 보조 주사라고하며 현재 자동 대거에서 지원하지 않습니다.

당신은 공장 패턴으로이 문제를 해결할 수 있습니다 : 당신이를 만들 필요가있을 때

class B { 
    @Inject AFactory aFactory; 

    //... 
} 

과 :

class AFactory { 
    @Inject DebugLogger debuggLogger; 

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

지금이 공장을 주입하고 A의 인스턴스를 생성하는 데 사용할 수 있습니다 A에 'amount'와 'frequency'를 사용하면 공장을 사용합니다.A 여전히 로거 예를 제공하기 위해 주입을 사용하는 동안 기록 장치, 양, 및 주파수 필드 final 인스턴스를 가질 수 있도록

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

이 있습니다.

Guice에는 본질적으로 이러한 공장의 생성을 자동화하는 보조 주입 플러그인이 있습니다. Dagger 메일 링리스트에 have been discussion이 추가되었지만이 글을 작성하는 시점에는 아무 것도 결정되지 않았습니다.

+0

빠른 응답 감사합니다. 설명 된 팩터 리 패턴은 최상의 접근 방법처럼 보입니다. 또한 Dagger가 개인 필드 주입을 지원하면 쉽게 달성하려는 것을 허용 할 수 있다고 생각했습니다. Dagger의 독창적 인 디자인의 일부가 아닌 이유가 궁금합니다. 대거가 반사를 사용하여 주사한다고 가정합니다. 그렇다면 사적인 필드는 문제가되지 않습니다. – Alex

+0

대거 (dagger)는 반사에 빠지 긴하지만 그것은 주된 주입 방법이 아닙니다. 필드를 직접 설정하거나 생성자를 호출하는 코드를 생성합니다. 따라서 소스 트리에서 다른 코드와 마찬가지로 작동하며 'private'멤버에 액세스 할 수 없습니다. –

+0

그리고 일부 보안 관리자는 클래스의 액세스 가능성 변경에 의존하는 리플렉션을 중단합니다. –

3

Jake의 게시물은 완전히 사실이라고 말합니다. 즉 Guice와 Dagger와 함께 일하는 Google 직원 중 일부는 Guice 나 Dagger 또는 독립 실행 형에서 사용할 수있는 "보조 분사"또는 자동 공장 생성에 대한 대안 버전을 개발 중입니다. 즉 팩토리 클래스 소스 코드를 생성합니다. 이 팩토리 클래스는 표준 JSR-330 클래스처럼 (필요한 경우) 주사 할 수 있습니다. 그러나 아직 풀려 나지 않았습니다.

이와 같은 해결 방법을 기다리면서 Jake Wharton의 접근 방식이 권장됩니다.

+0

그래, 걱정하지 않아도, 대거는 아직 초기 버전에 있음을 알 수 있습니다. 나는이 두 가지 기능 중 일부가 바이너리의 전체 크기를 줄이기위한 플러그인으로 개발되고있는 것을 좋아한다. (최소한 나에게는 안드로이드 개발을위한 매력적인 옵션이 될 것이다.) – Alex

+1

그래서 자동 공장 생성 : https://github.com/google/auto/tree/master/factory – phazei

3

생성자에 주사제와 주사제를 혼합하기 때문에 문제가 발생합니다. 당신에게 상심의 톤을 저장하고 깨끗한 코드를 유지합니다 주입을위한 일반적인 규칙은 다음과 같습니다

  1. 주사 가능한 자신의 생성자에서 다른 주사제를 요청하지만 newables을 위해 할 수 있습니다.

  2. Newables는 생성자에서 다른 newables를 요청할 수 있지만 주사제는 요구할 수 없습니다.

주사 가능한 서비스 유형 오브젝트, 즉 같은 등 CreditCardProcessor, MusicPlayer, 같은 일을 할 개체 값 유형이 등

0

제이크의 포스트 크레딧 카드, 노래로 객체입니다

Newables 훌륭하지만 더 간단한 방법이 있습니다. Google은 컴파일시 자동으로 공장을 만들기 위해 AutoFactory 라이브러리를 만들었습니다.

첫째, 인수를 주입 @AutoFactory 주석과 @Provided 주석 클래스 A를 만듭니다

@AutoFactory 
public class A { 

    private DebugLogger logger; 

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

그런 다음 라이브러리는 컴파일시에 AFactory 클래스를 만듭니다. 따라서 B 클래스의 생성자에 팩토리를 삽입해야합니다.

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); 
    } 
} 
관련 문제