의존성 주입 이해


109

의존성 주입 (DI) 에 대해 읽고 있습니다. 필자가 읽는 동안 제어 역전 (IoC)을 참조하고 있었기 때문에 여행을 갈 수 있다고 느꼈습니다.

이것은 내 이해입니다 : 모델을 소비하는 클래스에서 모델을 만드는 대신 모델을 이미 흥미로운 속성으로 채워진 모델에 매개 변수로 사용할 수있는 새 클래스에 전달합니다 (주입) 생성자).

나에게 이것은 단지 인수를 전달하는 것입니다. 요점을 이해하지 못했을까요? 더 큰 프로젝트에서 더 분명해 질까요?

내 이해는 비 의사 (의사 코드 사용)입니다.

public void Start()
{
    MyClass class = new MyClass();
}

...

public MyClass()
{
    this.MyInterface = new MyInterface(); 
}

그리고 DI는

public void Start()
{
    MyInterface myInterface = new MyInterface();
    MyClass class = new MyClass(myInterface);
}

...

public MyClass(MyInterface myInterface)
{
    this.MyInterface = myInterface; 
}

내가 여기에 장애물에 있다고 확신 할 때 누군가가 약간의 빛을 비출 수 있습니까?


5
MyInterface와 MyClass 중 5 개를 사용하고 종속성 체인을 형성하는 여러 클래스가 있습니다. 모든 것을 설정하는 것은 엉덩이에 고통이됩니다.
Euphoric

159
" 나에게 이것은 단지 논증을 전달하는 것입니다. 요점을 오해 했어야합니까? " 맞아요. 그것은 "의존성 주입"입니다. 이제 간단한 개념을 위해 다른 미친 전문 용어를 찾을 수 있습니다. 재미 있습니다!
Eric Lippert

8
나는 Lippert 씨의 의견을 충분히지지 할 수 없습니다. 나도 어쨌든 내가 자연스럽게하고있는 일에 대해 혼란스러운 전문 용어임을 알았습니다.
Alex Humphrey

16
@EricLippert : 정확하지만 약간 환원 적이라고 생각합니다. Parameters-as-DI는 데이터를 전달하는 데 익숙한 평균 명령형 / OO 프로그래머에게 즉각적인 개념이 아닙니다. 여기 DI는 행동 을 전달할 수있게 해주므로 평범한 프로그래머보다 더 유연한 세계관이 필요합니다.
Phoshi

14
@Phoshi : 당신은 좋은 지적을했지만, 왜 "종속성 주입"이 처음에 좋은 생각인지에 대해 회의적인 이유에 손가락을 넣었습니다. 내가 따라 클래스를 작성하는 경우 의 정확성 및 성능을 다른 클래스의 행동을 한 후 마지막으로 내가하고 싶은 일이 제대로 구성 및 종속성을 구성을 담당하는 클래스의 사용자를 만들 것입니다! 그게 직업 이야 소비자가 의존성을 주입하도록 할 때마다 소비자가 잘못했을 수있는 지점을 만듭니다.
Eric Lippert

답변:


106

예, 생성자 또는 속성을 통해 종속성을 주입합니다.
그 이유 중 하나는 MyInterface의 인스턴스를 구성하는 방법에 대한 세부 사항으로 MyClass를 방해하지 않기 때문입니다. MyInterface는 자체적으로 전체 종속성 목록이있는 것일 수 있으며 MyClass 내부의 모든 MyInterface 종속성을 인스턴스화하면 MyClass 코드가 매우 빠릅니다.

또 다른 이유는 테스트 때문입니다.
파일 리더 인터페이스에 대한 종속성이 있고 ConsumerClass와 같은 생성자를 통해이 종속성을 삽입하는 경우 테스트 중에 파일 리더의 인 메모리 구현을 ConsumerClass에 전달할 수 있습니다. 테스트 중에 I / O를 수행하십시오.


13
이것은 훌륭하고 잘 설명되어 있습니다. 내 담당자가 아직 허용하지 않으므로 가상 +1을 수락하십시오!
MyDaftQuestions 2013 년

11
복잡한 시공 시나리오는 팩토리 패턴 사용에 적합한 후보입니다. 이런 식으로, 팩토리는 객체를 생성하는 책임을 가지므로 클래스 자체가 필요하지 않으며 단일 책임 원칙을 고수합니다.
매트 깁슨

1
@MattGibson 좋은 지적.
Stefan Billiet

2
테스트의 경우 +1, 잘 구조화 된 DI는 테스트 속도를 높이고 테스트중인 클래스에 포함 된 단위 테스트를 수행하는 데 도움이됩니다.
Umur Kontacı

52

DI를 구현하는 방법은 사용되는 언어에 따라 다릅니다.

다음은 간단한 비 DI 예제입니다.

class Foo {
    private Bar bar;
    private Qux qux;

    public Foo() {
        bar = new Bar();
        qux = new Qux();
    }
}

예를 들어 테스트를 위해 mock 객체를 사용하고 싶습니다 bar. 따라서 우리는 이것을보다 유연하게 만들 수 있으며 생성자를 통해 인스턴스를 전달할 수 있습니다.

class Foo {
    private Bar bar;
    private Qux qux;

    public Foo(Bar bar, Qux qux) {
        this.bar = bar;
        this.qux = qux;
    }
}

// in production:
new Foo(new Bar(), new Qux());
// in test:
new Foo(new BarMock(), new Qux());

이것은 이미 가장 간단한 형태의 의존성 주입입니다. 그러나 이것은 모든 것이 수동으로 이루어져야하기 때문에 여전히 짜증납니다 (발신자가 내부 객체에 대한 참조를 보유하여 상태를 무효화 할 수 있기 때문에).

팩토리를 사용하여 더 많은 추상화를 도입 할 수 있습니다.

  • 하나의 옵션은 Foo추상 팩토리에 의해 생성되는 것입니다.

    interface FooFactory {
        public Foo makeFoo();
    }
    
    class ProductionFooFactory implements FooFactory {
        public Foo makeFoo() { return new Foo(new Bar(), new Baz()) }
    }
    
    class TestFooFactory implements FooFactory {
        public Foo makeFoo() { return new Foo(new BarMock(), new Baz()) }
    }
    
    FooFactory fac = ...; // depends on test or production
    Foo foo = fac.makeFoo();
    
  • 또 다른 옵션은 팩토리를 생성자에게 전달하는 것입니다.

    interface DependencyManager {
        public Bar makeBar();
        public Qux makeQux();
    }
    class ProductionDM implements DependencyManager {
        public Bar makeBar() { return new Bar() }
        public Qux makeQux() { return new Qux() }
    }
    class TestDM implements DependencyManager {
        public Bar makeBar() { return new BarMock() }
        public Qux makeQux() { return new Qux() }
    }
    
    class Foo {
        private Bar bar;
        private Qux qux;
    
        public Foo(DependencyManager dm) {
            bar = dm.makeBar();
            qux = dm.makeQux();
        }
    }
    

이것에 대한 나머지 문제 DependencyManager는 각 구성에 대해 새로운 서브 클래스 를 작성해야하고 관리 할 수있는 종속성의 수가 상당히 제한되어 있다는 것입니다 (각 새로운 종속성에는 인터페이스에서 새로운 메소드가 필요함).

리플렉션 및 동적 클래스 로딩과 같은 기능을 사용하여이를 피할 수 있습니다. 그러나 이것은 사용되는 언어에 크게 의존합니다. Perl에서 클래스는 이름으로 참조 할 수 있으며 그냥 할 수 있습니다.

package Foo {
    use signatures;

    sub new($class, $dm) {
        return bless {
            bar => $dm->{bar}->new,
            qux => $dm->{qux}->new,
        } => $class;
    }
}

my $prod = { bar => 'My::Bar', qux => 'My::Qux' };
my $test = { bar => 'BarMock', qux => 'QuxMock' };
$test->{bar} = 'OtherBarMock';  # change conf at runtime

my $foo = Foo->new(rand > 0.5 ? $prod : $test);

Java와 같은 언어에서 종속성 관리자가 다음과 유사하게 작동하도록 할 수 있습니다 Map<Class, Object>.

Bar bar = dm.make(Bar.class);

어떤 실제 클래스에 대한 Bar.class해결은 런타임에 구성 될 수 있습니다 (예 : Map<Class, Class>인터페이스를 구현에 매핑 하는 유지 보수를 통해) .

Map<Class, Class> dependencies = ...;

public <T> T make(Class<T> c) throws ... {
    // plus a lot more error checking...
    return dependencies.get(c).newInstance();
}

생성자를 작성하는 데 여전히 수동 요소가 있습니다. 그러나 주석을 통해 DI를 구동하여 생성자를 완전히 불필요하게 만들 수 있습니다.

class Foo {
    @Inject(Bar.class)
    private Bar bar;

    @Inject(Qux.class)
    private Qux qux;

    ...
}

dm.make(Foo.class);  // takes care of initializing "bar" and "qux"

다음은 작은 (그리고 매우 제한된) DI 프레임 워크의 구현 예제입니다 : http://ideone.com/b2ubuF . 비록이 구현은 불변 개체에 대해 완전히 사용할 수 없지만 (이 순진한 구현은 생성자에 대한 매개 변수를 취할 수 없습니다).


7
이 예제는 훌륭하지만, 모두 소화하기 위해 몇 번 읽어야하지만 시간이 많이 걸리는 것에 감사드립니다.
MyDaftQuestions

33
그것은 다음의 각 단순화 더 복잡 얼마나 재미
Kromster

48

스택 오버플로에 대한 우리의 친구는 이것에 대한 좋은 대답을 가지고 있습니다 . 내가 가장 좋아하는 것은 인용문을 포함한 두 번째 대답입니다.

"의존성 주입"은 5 센트 개념의 25 달러 용어입니다. (...) 의존성 주입은 객체에 인스턴스 변수를 제공하는 것을 의미합니다. (...).

에서 제임스 쇼어의 블로그 . 물론, 이것 위에 더 복잡한 버전 / 패턴이 있지만, 기본적으로 무슨 일이 일어나고 있는지 이해하면 충분합니다. 자체 인스턴스 변수를 생성하는 객체 대신 외부에서 전달됩니다.


10
나는 이것을 읽었고 ... 지금까지는 말이되지 않았다 ... 지금은 말이됩니다. 의존성은 실제로 (어떻게 강력한 지에 관계없이) 이해해야 할 큰 개념은 아니지만 큰 이름과 많은 인터넷 소음이 있습니다!
MyDaftQuestions

18

거실에서 인테리어 디자인을하고 있다고 가정 해보십시오. 천장에 멋진 샹들리에를 설치하고 고급스러운 플로어 램프를 연결하십시오. 1 년 후 당신의 사랑스러운 아내는 그녀가 방을 덜 덜 공식적으로 결정하기로 결정했습니다. 어떤 조명기구를 쉽게 교체 할 수 있습니까?

DI의 기본 개념은 어디에서나 "플러그인"방식을 사용하는 것 입니다. 마른 벽에서 나온 벽이없고 모든 전선이 노출 된 단순한 오두막집에 사는 경우에는 큰 일이 아닙니다. (당신은 핸디입니다-당신은 아무것도 바꿀 수 있습니다!) 그리고 마찬가지로, 작고 간단한 응용 프로그램에서 DI는 가치보다 더 많은 합병증을 추가 할 수 있습니다.

그러나 크고 복잡한 응용 분야의 경우 DI는 장기 유지 관리에 없어서는 안될 필수 요소 입니다. 이를 통해 코드 (예 : 데이터베이스 백엔드)에서 전체 모듈을 "분리"하고 동일한 기능을 수행하지만 완전히 다른 방식 (예 : 클라우드 스토리지 시스템)으로 수행하는 다른 모듈과 교환 할 수 있습니다.

BTW, DI에 대한 논의는 Mark Seeman의 .NET에서 Dependency Injection을 권장하지 않으면 불완전 합니다. .NET에 익숙하고 대규모 SW 개발에 참여하려는 경우이 내용을 반드시 읽어보십시오. 그는 그 개념을 내가 할 수있는 것보다 훨씬 잘 설명합니다.

코드 예제가 간과하는 DI의 마지막 필수 특성 중 하나를 소개하겠습니다. DI를 사용하면 " 프로그램 인터페이스 "라는 격언에 캡슐화되어있는 광범위한 유연성을 활용할 수 있습니다 . 예제를 약간 수정하려면 :

public void Main()
{
    ILightFixture fixture = new ClassyChandelier();
    MyRoom room = new MyRoom (fixture);
}
...
public MyRoom(ILightFixture fixture)
{
    this.MyLightFixture = fixture ; 
}

그러나 MyRoom이제는 ILightFixture 인터페이스로 설계되었으므로 내년에 쉽게 들어가서 Main 함수에서 한 줄을 변경할 수 있고 ( "인 젝트"가 다른 "종속성"), MyRoom에서 새로운 기능을 즉시 얻을 수 있습니다. 별도의 라이브러리에있는 경우 MyRoom을 다시 빌드하거나 재배치합니다 (물론 모든 ILightFixture 구현은 Liskov 대체를 염두에두고 설계되었다고 가정합니다). 간단한 변경만으로 모두 :

public void Main()
{
    ILightFixture fixture = new MuchLessFormalLightFixture();
    MyRoom room = new MyRoom (fixture);
}

게다가, 지금은 단위 테스트 할 수 있습니다 MyRoom(당신이 있는 바로?, 단위 테스트)와 저를 테스트 할 수있는 "모의"조명기구, 사용 MyRoom의 완전히 독립적ClassyChandelier.

물론 더 많은 장점이 있지만 아이디어를 나에게 팔 았던 것입니다.


감사의 말을 전하고 싶었지만 도움이 되길 원했지만 개선을 제안하기 위해 의견을 사용하기를 원하므로 느낌이 들면 언급 한 다른 이점 중 하나를 추가 할 수 있습니다. 그러나 그렇지 않으면 감사합니다. 이것이 나에게 도움이되었습니다.
Steve

2
나는 또한 당신이 언급 한 책에 관심이 있지만,이 주제에 관한 584 페이지의 책이 있다는 것은 놀라운 일입니다.
Steve

4

의존성 주입은 매개 변수를 전달하는 것만으로도 여전히 몇 가지 문제가 발생하며, 이름은 대상을 만드는 것과 전달하는 것의 차이점이 왜 중요한지에 중점을 둡니다.

객체 A가 유형 B를 호출 할 때마다 (아주 분명히) 중요하며, A는 B의 작동 방식에 달려 있습니다. 즉, A가 구체적으로 사용할 B의 구체적인 유형을 결정하면 A를 수정하지 않고 외부 클래스에서 A를 사용하는 방법에 대한 많은 유연성이 손실됩니다.

나는 당신이 의존성 주입의 "요점을 놓친 것"에 대해 이야기하기 때문에 이것을 언급합니다. 이것이 사람들이 그것을 좋아하는 이유의 많은 부분입니다. 매개 변수를 전달한다는 의미 일 수도 있지만 그렇게하는지 여부가 중요 할 수 있습니다.

또한 실제로 DI를 구현하는 데 어려움이 있으면 대규모 프로젝트에서 더 잘 나타납니다. 일반적으로 어떤 구체적인 클래스를 사용할 것인지에 대한 실제 결정을 맨 처음으로 옮기므로 start 메소드는 다음과 같습니다.

public void Start()
{
    MySubSubInterfaceA mySubSubInterfaceA = new mySubSubInterfaceA();
    MySubSubInterfaceB mySubSubInterfaceB = new mySubSubInterfaceB();     
    MySubInterface mySubInterface = new MySubInterface(mySubSubInterfaceA,mySubSubInterfaceB);
    MyInterface myInterface = new MyInterface(MySubInterface);
    MyClass class = new MyClass(myInterface);
}

그러나 이런 종류의 리벳 팅 코드의 또 다른 400 줄로. 시간이 지남에 따라 DI 컨테이너의 매력은 더욱 분명해집니다.

또한 IDisposable을 구현하는 클래스를 상상해보십시오. 그것을 사용하는 클래스가 스스로 작성하는 대신 주입을 통해 가져 오면 Dispose ()를 호출 할 때를 어떻게 알 수 있습니까? (DI 컨테이너가 다루는 또 다른 문제는 무엇입니까?)

따라서 의존성 주입은 매개 변수를 전달하는 것이지만 사람들이 의존성 주입을 말할 때 "매개 변수를 객체에 전달하는 것 vs 객체가 무언가를 인스턴스화하는 대안"을 의미하며, DI 컨테이너의 도움으로 이러한 접근 방식.


그것은 많은 서브와 인터페이스입니다! 인터페이스를 구현하는 클래스 대신 인터페이스를 새로 작성 하시겠습니까? 잘못된 것 같습니다.
JBR 윌킨슨

@JBRWilkinson 인터페이스를 구현하는 클래스를 의미합니다. 더 명확하게해야합니다. 그러나 중요한 점은 큰 앱에는 종속성이있는 클래스가 있고 결국에는 종속성이있는 종속성이 있다는 것입니다. Main 메서드에서 수동으로 설정하면 많은 생성자 주입이 수행됩니다.
psr

4

수업 수준에서는 쉽습니다.

'종속성 주입'은 단순히 "내 동료를 어떻게 찾습니까?"라는 질문에 "그들은 당신에게 강요 당할 것입니다-당신은 가서 직접 갈 필요가 없습니다"라는 질문에 대답합니다. ( '입력에 대한 조작을 어떻게 주문합니까?'라는 질문이 비슷한 '제어의 반전'과 유사하지만 동일하지는 않습니다).

만을 가진 공동 작업자가 당신을 밀어 있다는 이점은 당신이 임의하지 않은 개인적으로 결정하여 그래프의 모양과 가변성을 사전에 결정 ... 현재의 요구에 맞는 객체 그래프를 구성하기 위해 클래스를 사용하는 클라이언트 코드를 수 공동 작업자의 구체적인 유형과 수명주기

(DI가 자연스럽게 인터페이스의 사용을 장려하고 있지만, 테스트 가능성, 느슨한 결합 등의 다른 모든 이점은 인터페이스 사용에서 크게 따르지 않고 의존성 주입에서 크게 따르지 않습니다).

당신이 당신의 자신의 협력자를 인스턴스화하지 않도록 경우, 클래스가해야한다는 주목할 가치가있다 , 따라서 그것은하지 ... (이 후자의 옵션은 종종 방법에 의해 간과 생성자, 속성 또는 메소드의 인수에서 공동 작업자를 얻을 수 클래스의 공동 작업자가 '상태'의 일부가되는 것이 항상 의미가있는 것은 아닙니다.)

그리고 그것은 좋은 것입니다.

응용 프로그램 수준에서 ...

사물에 대한 학급 별 관점에서 볼 때 많은 것들이 있습니다. "자신의 공동 작업자를 인스턴스화하지 마십시오"규칙을 따르는 많은 클래스가 있다고 가정 해 보겠습니다. 가장 간단한 방법은 좋은 오래된 코드 (생성자, 속성 및 메서드를 호출하는 데 유용한 도구입니다!)를 사용하여 원하는 객체 그래프를 작성하고 입력을하는 것입니다. (그래, 그래프의 일부 객체는 그 자체가 객체 팩토리가 될 것입니다.이 팩토리는 그래프의 다른 오래 지속되는 객체에 협력자로 전달되어 서비스 준비가되었습니다 ... 모든 객체를 사전 구성 할 수는 없습니다! ).

... 앱의 객체 그래프를 '유연하게'구성해야합니다 ...

코드 관련이 아닌 다른 목표에 따라 최종 사용자에게 배포 된 개체 그래프를 제어 할 수 있습니다. 이는 이름 / 값 쌍이 포함 된 자체 디자인의 텍스트 파일, XML 파일, 사용자 정의 DSL, 선언적 그래프 설명 언어와 같은 구성 체계의 방향으로 안내합니다. YAML, JavaScript와 같은 명령적인 스크립팅 언어 또는 현재 작업에 적합한 다른 것. 사용자의 요구를 충족시키는 방식으로 유효한 객체 그래프를 작성하는 데 필요한 모든 것.

... 상당한 설계 힘이 될 수 있습니다.

해당 유형 의 가장 극단적 인 상황에서 매우 일반적인 접근 방식을 선택하고 최종 사용자 에게 선택한 객체 그래프를 '배선' 할 수있는 일반적인 메커니즘 을 제공하고 인터페이스에 대한 구체적인 인터페이스 구현을 제공 할 수도 있습니다 . 실행 시간! (문서는 번쩍이는 보석이며, 사용자는 매우 똑똑하고 최소한 응용 프로그램 객체 그래프의 대략적인 개요에 익숙하지만 컴파일러를 사용하지는 않습니다.) 이 시나리오는 이론적으로 일부 '엔터프라이즈'상황에서 발생할 수 있습니다.

이 경우 사용자는 모든 유형, 해당 유형의 객체 그래프 구성 및 신화적인 최종 사용자가 혼합하고 일치시킬 수있는 인터페이스 팔레트를 표현할 수있는 선언적 언어가있을 수 있습니다. 사용자의인지 부하를 줄이려면 '컨벤션 별 구성'접근 방식을 선호하므로 사용자는 전체적인 문제를 해결하기보다는 관심있는 객체-그래프-조각 조각을 시작하고 무시하기 만하면됩니다.

당신은 가난한 잔디!

직접 작성하지는 않았지만 (심지어 언어에 대한 YAML 바인딩을 확인하십시오) 일종의 DI 프레임 워크를 사용하고 있습니다.

해당 프레임 워크의 성숙도에 따라 생성자 주입을 사용할 수있는 옵션이 없을 수도 있습니다 (협업자가 객체 수명 기간 동안 변경되지 않음). 공동 작업자는 개체의 수명 기간 동안 변경되지 않으며 인터페이스의 모든 구체적인 구현에 특정 유형의 공동 작업자가 있어야 하는 논리적 이유가없는 경우에도 마찬가지 입니다. 그렇다면 코드베이스 전체에서 부지런히 '인터페이스 사용'을 했음에도 불구하고 현재 강력한 결합 지옥에 빠져 있습니다. 공포!

다행스럽게도 생성자 주입 옵션을 제공하는 DI 프레임 워크를 사용했으며 사용자가 구성하는 데 필요한 특정 사항에 대해 생각하고 작업에 더 적합한 UI를 제공하는 데 더 많은 시간을 소비하지 않아 사용자가 약간 심술 are 습니다. 손에. (공평하지만, 당신은 아마도 방법을 생각하려고했지만 JavaEE는 당신을 실망 시키고이 끔찍한 핵에 의존해야했습니다).

부트 노트

코드를 작성하여 객체 그래프를 코드로 작성하는 작업을 생략 할 수 있는 방법을 제공하는 Google Guice를 사용 하는 것은 결코 아닙니다. 아아!


0

많은 사람들이 여기서 DI는 인스턴스 변수의 초기화를 아웃소싱하는 메커니즘이라고 말했다.

명확하고 간단한 예에서는 실제로 "종속성 주입"이 필요하지 않습니다. 문제에서 언급했듯이 생성자에 전달하는 간단한 매개 변수 로이 작업을 수행 할 수 있습니다.

그러나 응용 프로그램 수준의 리소스에 대해 말할 때는 전용 "종속성 주입"메커니즘이 유용하다고 생각합니다. 발신자와 수신자 모두 이러한 리소스에 대해 전혀 모르고 있습니다.

예 : 데이터베이스 연결, 보안 컨텍스트, 로깅 기능, 구성 파일 등

또한 일반적으로 모든 싱글 톤은 DI에 적합한 후보입니다. 당신이 가지고 있고 더 많이 사용할수록 DI가 필요할 확률이 높아집니다.

이러한 종류의 자원의 경우 매개 변수 목록을 통해 모든 것을 이동하는 것이 의미가 없으므로 DI가 발명되었다는 것이 분명합니다.

마지막으로, 실제 주입을 수행하는 DI 컨테이너도 필요합니다 (다시 말하면 구현 세부 사항에서 발신자와 수신자를 모두 해제합니다).


-2

추상 참조 형식을 전달하면 호출 코드는 추상 형식을 확장 / 구현하는 모든 개체를 전달할 수 있습니다. 따라서 앞으로 객체를 사용하는 클래스는 코드를 수정하지 않고 만들어진 새로운 객체를 사용할 수 있습니다.


-4

이것은 amon의 답변 외에도 다른 옵션입니다

빌더 사용 :

푸 클래스 {
    전용 바 바;
    개인 Qux qux;

    개인 Foo () {
    }

    공개 푸 (빌더 빌더) {
        this.bar = builder.bar;
        this.qux = builder.qux;
    }

    공개 정적 클래스 빌더 {
        전용 바 바;
        개인 Qux qux;
        퍼블릭 뷰더 setBar (Bar bar) {
            this.bar = 바;
            이것을 돌려줍니다;
        }
        공공 건축업자 setQux (Qux qux) {
            this.qux = qux;
            이것을 돌려줍니다;
        }
        공개 푸 빌드 () {
            새로운 Foo (this);
        }
    }
}

// 생산 중 :
Foo foo = 새로운 Foo.Builder ()
    .setBar (new Bar ())
    .setQux (새 Qux ())
    .짓다();

// 테스트 중 :
Foo testFoo = 새로운 Foo.Builder ()
    .setBar (new BarMock ())
    .setQux (새 Qux ())
    .짓다();

이것이 내가 의존성에 사용하는 것입니다. DI 프레임 워크를 사용하지 않습니다. 그들은 방해가됩니다.


4
이것은 1 년 전의 다른 답변에별로 도움이되지 않으며 빌더가 의존자가 주입을 이해하는 데 도움이되는 이유에 대한 설명을 제공하지 않습니다 .

@Snowman DI 대신 다른 옵션입니다. 그렇게 보이지 않아서 미안합니다. 다운 투표 해 주셔서 감사합니다.
user3155341

1
AS는 매개 변수를 전달하는 것만 큼 DI가 실제로 간단한 지 이해하기를 원했지만 DI에 대한 대안을 요구하지 않았습니다.

1
OP의 예는 매우 좋습니다. 귀하의 예는 단순한 아이디어에 더 복잡한 것을 제공합니다. 그들은 문제의 문제를 설명하지 않습니다.
Adam Zuckerman

생성자 DI가 매개 변수를 전달 중입니다. 구체적인 클래스 객체 대신 인터페이스 객체를 전달하는 것을 제외하고. DI를 통해 해결되는 구체적인 구현에 대한 '의존성'입니다. 생성자는 전달되는 유형을 알지 못하거나 신경 쓰지 않으며 인터페이스를 구현하는 유형을 수신하고 있음을 알뿐입니다. PluralSight를 추천합니다. 훌륭한 초보자 DI / IOC 코스가 있습니다.
Hardgraf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.