Effective Java - Item 5

초보개발·2022년 8월 8일
0

JAVA

목록 보기
13/15

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

대부분 클래스에서는 여러개의 자원에 의존한다. 여기서는 SpellChecker(맞춤법 검사기)는 Dictionary(사전)를 사용하고 이것을 의존성이라고 한다.

잘못 사용한 예시들

static 유틸리티

정적 유틸리티를 잘못 사용한 예 - 유연하지 않고 테스트하기 어려움

interface Lexicon {}

class KoreanDicationary implements Lexicon {}

public class SpellChecker {

    private static final Lexicon dictionary = new KoreanDicationary(); // 한국어 사전

    private SpellChecker() {} // 인스턴스를 가질 수 없다.

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

Singleton

싱글톤을 잘못 사용한 예 - 유연하지 않고 테스트하기 어려움

public class SpellChecker {

    private final Lexicon dictionary = new KoreanDicationary();

    private SpellChecker() {}

    public static final SpellChecker INSTANCE = new SpellChecker(...);

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

}

현실적으로 사전은 언어별로 여러개 존재하기 때문에 한가지의 사전만을 사용한다는 가정은 좋지 않다. 또한, 테스트시에는 테스트용 사전을 사용할 수도 있다.
위의 코드에서 final을 제거하고 사용한다면 다른 사전을 사용할 수 있지만 값이 변할 수 있기 때문에 멀티스레딩 환경에서는 적합하지 않다.
SpellChecker 클래스가 여러 자원 인스턴스를 지원해야 하고 클라이언트가 원하는 Dictionary를 사용하게끔 하면 된다. 즉, 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식이다.

적절한 예 - 의존 객체 주입 패턴

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) {...}
}

기존과 달리 하나의 사전 타입에만 의존하지 않고, Lexicon의 구현체라면 주입받아 사용할 수 있으며 final 한정자를 사용하여 불변성을 띄고 여러 클라이언트가 의존 객체들을 안심하고 공유할 수 있다.
의존 객체 주입은 생성자, 정적 팩토리(아이템1), 빌더(아이템2)에도 적용 가능하다.

이 패턴의 변형으로 생성자에 자원 팩터리를 넘겨주는 방식이 있다.

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

이 예로, JAVA8의 Supplier<T> 인터페이스가 팩터리를 표현한 완벽한 예시이다. Supplier<T>를 매개변수로 받는 메서드는 일반적으로 bounded wildcard type(아이템31)으로 매개변수을 제한해야 한다.

// 클라이언트가 제공한 팩터리가 생성한 Tile로 구성된 Mosaic를 만드는 메서드
Mosaic create(Supplier<? extends Tile> tileFactory) {...}

의존 객체 주입이 유연성과 테스트 용이성을 향상시켜 주긴하지만 의존성이 많은 큰 프로젝트에서는 코드를 어지럽게 만들 수 있다. (Spring 같은 프레임워크를 사용하면 이런 단점을 해결할 수 있다.)

의존하는 자원에 따라 행동이 달라지는 클래스를 생성할 경우, 정적 유틸리티 클래스나 싱글톤은 사용하지 않는 것이 좋다. 대신, 리소스를 생성자, 팩토리로 전달하는 의존성 주입을 사용하여 클래스의 유연성, 재사용성, 테스트 용이성을 개선시킨다.

0개의 댓글