///
Search
💡

item 5. 자원을 직접 명시하지 말고 의존객체 주입을 사용하라 Prefer dependency injection to hardwiring resources

싱글턴 / 유틸리티 클래스의 문제점

1.
유틸리티 클래스 혹은 싱글턴 클래스는 상태를 가지면 멀티스레딩 환경에서 버그를 불러일으킬 수 있다.
2.
테스트 하기가 어렵다. 싱글턴이 테스트 하기 어려운 예시는 여기
3.
객체가 스스로 가지고 있는 자원(underlying resource)을 가지고 객체의 행동을 수행한다는 객체지향의 개념과 멀어진다.

사례로 알아보기

유틸리티 철자 검사기

// Inappropriate use of static utility - inflexible & untestable! public class SpellChecker { private static final Lexicon dictionary = ...; private SpellChecker() {} // Noninstantiable public static boolean isValid(String word) { ... } public static List<String> suggestions(String typo) { ... } }
Java
복사

싱글톤 철자 검사기

// Inappropriate use of singleton - inflexible & untestable! public class SpellChecker { private final Lexicon dictionary = ...; private SpellChecker(...) {} public static INSTANCE = new SpellChecker(...); public boolean isValid(String word) { ... } public List<String> suggestions(String typo) { ... } }
Java
복사
코드를 읽어보면 위 두개가 어떤 역할을 하는 객체인지 알 수 있다. 사전을 참고삼아 철자 검사를 해주는 기능을 가지고 있다. 하지만 영어 검사만 가능하던 철자검사기에 한글 검사를 추가하려면? 새로운 dictionary가 필요할 것이다. 인스턴스 생성 없이 스태틱 객체로 직접 할당을 하던 위 두 방식에선 dictionary를 바꾸기가 쉽지 않다. setDictionary(); 와 같은 메서드를 만들어 정적 멤버를 변경한다면, 멀티 스레딩 환경에서 버그를 유발하기 매우 쉽다. 그렇기 때문에 내부 멤버에 의해 다른 행동을 할 수 있는 객체는 싱글턴이나 유틸리티 객체보다, 의존성 주입방식으로 코딩하는 게 낫다.

의존 주입 방식으로 변경한 철자 검사기

// Dependency injection provides flexibility and testability 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) { ... } }
Java
복사
이 의존성 주입 방식이라는 것은 개념상 매우 간단하여 모든 개발자가 사용하는 줄도 모르고 사용하고 있는 패턴이다. 의존성 주입은 테스트 용이하며 유연한 객체를 만들어 준다. 불변성을 지키게 도와주고, 그렇기에 여러 객체가 불변한 동일객체에 대한 참조를 공유할 수 도 있다. 객체를 객체답게 활용할 배경이 되는 것은 덤이다.

의존 주입 방식을 활용한 좋은 패턴 - 참조할 자원의 팩토리를 주입하기

자바8에서 제공되는 Supplier<T>를 이용해 팩토리를 주입함으로써 더더욱 유연한 객체생성을 지원할 수 있다. 모자이크 객체를 생성할 때 타일 객체가 필요하다고 생각해보자. 타일 객체 자체를 주입할 수 도 있지만, 타일 팩토리를 주입해도 좋지 않겠는가?
Mosaic create(Supplier<? extends Tile> tileFactory) { ... }
Java
복사

문제점은 없어?

의존 주입방식은 필연적으로 의존성을 주입해줄 객체가 필요하다. 큰 규모의 프로젝트에선 의존성을 주입해주는 코드와 받는 코드의 스파게티 코드가 등장하기 쉬운데, 이를 막아주는 다양한 의존성 관리 프레임워크가 존재한다. Dagger, Guice, Spring 등. 맘에 드는 의존 주입 프레임워크를 골라 사용하면 유연하고 테스트 가능한 코드를 작성하고, 비즈니스 로직에 집중할 수 있다.

용어정리

한글명
영어명
예외발생 가능성이 있는
error-prone
의존성 주입
dependency injection