[Effective Java] Item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라.

2022년 03월 03일

TOC

이 글은 Effective Java 3/E의 내용을 요약한 글입니다. 자세한 내용은 책을 참고하시기 바랍니다.

정적 유틸리티와 싱글턴의 단점

대부분의 많은 클래스들은 하나의 자원에 의존합니다.

// 정적 유틸리티를 잘못 사용한 예
public class SpellChecker {
    private static final Lexicon dictionary = ...;

    private SpellChecker() {}

    public static boolean isValid(String word) {...}
    public static List<String> suggestions(String typo) {...}
}
// 싱글턴 패턴을 잘못 사용한 예
public class SpellChecker {
    private final Lexicon dictionary = ...;

    private SpellChecker() {}
    public static SpellChecker getInstance() = new SpellChecker(..);

    public boolean isValid(String word) {...}
    public List<String> suggestions(String typo) {...}
}

위의 두 코드는 사전을 정적 유틸리티와 싱글턴을 통해 구현한 각각의 코드입니다. 작은 단위의 작업을 하는 유틸리티 클래스 또는 싱글턴 객체일 경우 위와 같은 코드는 나쁘지 않습니다. 하지만 위의 예시로 들어진 사전과 같이 하는 작업이 큰 경우(언어별, 특수 어휘용 등으로 분류 가능)에는 하나의 사전에서 모든 일을 하는 것을 효율적이지 않습니다.

의존 객체 주입 패턴

이와 같이 사용하는 자원에 따라 동작이 달라지는 클래스들은 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않습니다. 이러한 경우에는 클래스가 단일 인스턴스가 아닌 여러 인스턴스를 지원하고 사용자 입장에서 원하는 자원을 사용할 수 있도록 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용해야 합니다. (의존 객체 주입의 한 형태)

// 의존 객체 주입을 한 예
public class SpellChecker {
    private final Lexicon dictionary = ...;

    public SpellChecker(Lexicon dictionary) {
        this.dictionary = Objects.requireNonNull(dictionary);
    }

    public boolean isValid(String word) {...}
    public List<String> suggestions(String typo) {...}
}

위의 코드는 의존 객체 주입을 한 예시 코드입니다. 코드를 보면 위의 싱글턴과 정적 유틸리티 클래스를 통한 공통된 메서드 사용이 아닌 생성자에 원하는 자원(dictionary)를 넘겨주며 원하는 인스턴스를 만들고 그에 맞는 자원을 쓸 수 있습니다.

이러한 생성자 주입은 정적 팩터리와 빌더에도 모두 응용 가능하다.

팩터리 메서드 패턴

의존 객체 주입 패턴의 변형으로 생성자에게 자원 팩터리를 넘겨주는 팩터리 메서드 패턴(Factory Method Pattern)이 있습니다.

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

다음 인터페이스를 예시로 들자면 Supplier<T><T>를 통해 타입 매개변수를 제한하고, 클라이언트는 제한한 타입의 하위 타입이라면 무엇이든 생성할 수 있는 팩터리를 넘길 수 있습니다.

팩터리란? 호출될 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체

의존 객체 주입의 장단점

장점

  • 유연성, 재사용성, 테스트 용이성을 개선해줍니다. 단점
  • 의존성이 많은 큰 프로젝트에서는 코드를 어지럽게 만들기도 합니다.

    스프링(Spring), 대거(Dagger)와 같은 의존 객체 주입 프레임워크는 의존 객체를 직접 주입하도록 설계된 API를 사용하여 개발자의 불편함을 감소시켜준다.

핵심 정리

클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동작에 영향을 준다면 싱글턴과 정적 유틸리티 클래스는 사용하지 않는 것이 좋다. 이 자원들은 클래스가 직접 만들게 해서도 안 된다. 대신 필요한 자원을 생성자에 혹은 정적 팩터리나 빌더에 넘겨주도록 하는 것이 좋다. 의존 객체 주입이라 하는 이 기법은 클래스의 유연성, 재사용성, 테스트 용이성을 개선해준다.

Buy me a coffeeBuy me a coffee
Written by

@Seongwon

기술공유를 통해 새로운 가치 창조을 추구하는 백엔드 개발자 오성원입니다.
©SeongwonOh