Use Dependency Injection!

jiho·2021년 5월 23일
0

EffectiveJava

목록 보기
6/12

아마 IoC or DI 컨테이너 프레임워크를 한번 쯤 개발자들이 사용해봤을 것입니다. 각 클래스간에 의존성을 framework 수준에서 관리하게 해주는 프레임워크의 근간은 이번에 소개할 의존 객체 주입이라는 간단하면서도 강력한 방법입니다.

아마 기초 수준의 프로그래밍에서 벗어나기 시작할 쯤 마주치는 개념이라고 생각합니다.

자원을 직접 명시하지 말고 의존 객체 주입을 사용하라!

객체 지향 프로그래밍을 하다보면 클래스 간에 서로 의존성이 생길 수 밖에 없습니다. 그리고 대부분 처음에는 클래스의 생성자에서 맴버 변수에 new 키워드를 통해서 객체를 생성해서 할당하는 방식으로 객체간에 의존성을 만들어가게 됩니다.

class A {
	private B b;
    public A () {
    	b = new B();
    }
}

지금부터 위와 같은 방식의 단점을 생각해보겠습니다. B 클래스의 생성 방법이 달라지면 아마 b와 의존한 클래스 내용을 모두 수정해야 할 것 입니다. 즉, B 클래스의 변경에서 다른 클래스들이 자유로울 수가 없게되어 유지보수가 어려워지게됩니다. 그리고 만약 A클래스만을 테스트하는 테스트 코드 작성도 어려울 것입니다.
추가로 B 클래스 대신 C 클래스를 사용하는 A가 필요할 경우처럼 확장하기 어려운 상황입니다.

클래스가 여러 인스턴스를 지원하며, 클라이언트가 원하는 인스턴스를 사용할 수 있게 해줄 방법은
인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식입니다.

흔히 이러한 방식을 의존 객체 주입 패턴이라고 합니다.

Dependency Injection

class A {
	private final B b;
    public A(B b){
    	this.b = Object.requireNonNull(b);
    }
}

예에서는 B라는 타입하나만 사용했지만 자원이 몇개든 의존관계가 어떻든 잘 작동합니다. 또한 final을 통해 불변을 보장하기 때문에 여러 클라이언트가 의존 객체들을 안심하고 공유할 수 있습니다.

이러한 의존 객체 주입은 유연성테스트 용이성을 높여주게 됩니다.

이 패턴의 변형으로, 생성자에 팩토리를 넘겨주는 방식이 있습니다.
여기서 팩토리는 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체를 말합니다. 즉, 팩토리 메서드 패턴을 구현한 것을 합니다. 자바 8에서 소개된 Supplier<T> interface가 팩터리를 표현한 완벽한 예입니다.

Mosaic create(Supplier<? extends Tile> tailFactory){ ... }

위 예는 Tile의 하위 타입이라면 무엇이든 생성할 수 있는 Factory를 의존 객체로 주입받아서 factory에 의해 생성된 tile들로 구성되 Mosaic을 만드는 메서드입니다.

의존 객체 주입의 단점

말했듯이 테스트 용이성과 유연성을 개선해주긴 하지만 결국 의존성을 넣어주는 최종 코드도 있기 마련입니다. 아마 의존성을 관리하는 코드가 수천개나 되는 큰 프로젝트에서는 코드가 복잡할 것입니다. 하지만 Spring, Dagger, Guice 같은 의존 객체주입 프레임워크를 사용하면 대부분 그런 문제들은 해소됩니다.

정리

클래스가 내부적으로 하나이상의 자원에 의존하고 그 자원에 따라 클래스의 동작이 달라진다면 의존객체주입 방식을 따르는게 좋습니다. 필요한 자원들을 클래스가 직접 생성하기보다는 필요한 자원을 생성자에 넘겨주도록 하는 것이 좋습니다. 이 기법은 클래스의 유연성, 재사용성, 테스트 용이성을 기막히게 개선해줍니다.

여기서 테스트 용이성의 예로
테스트 코드에서 특정 객체를 Mock 객체로 생성해서 처리해줌으로써 원하는 형태로 테스트를 진행할 수 있게 해줍니다.

profile
Scratch, Under the hood, Initial version analysis

0개의 댓글