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 . 비록이 구현은 불변 개체에 대해 완전히 사용할 수 없지만 (이 순진한 구현은 생성자에 대한 매개 변수를 취할 수 없습니다).