테마.log/Effective Java

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

_2J 2022. 4. 18. 23:00

Effective Java 3/E

 

아이템5: 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

 

 

클래스들이 자원(다른 클래스)에 의존하는 경우가 있다.
예를 들어서 SpellChecker라는 클래스에서 dictionary라는 유틸리티 클래스를 사용한다고 가정해보겠다.

 

 

1. 정적 유틸리티 클래스

public class SpellChecker {
    private static final Lexicon dictionary = ...;
    
    private SpellChecker() {} // 인스턴스화 방지 (아이템 4 참고)
    
    public static boolean isVaild(String word) {...}
    public static List<String> suggestions(String typo) {...}
}

//사용은 이렇게!
SpellChecker.isValid(word);

 

 

2. 싱글턴

public class SpellChecker {
    private final Lexicon dictionary = ...;
    
    private SpellChecker() {} // 인스턴스화 방지 (아이템 4 참고)
    public static SpellChecker INSTANCE = new SpellChecker(...);
    
    public static boolean isVaild(String word) {...}
    public static List<String> suggestions(String typo) {...}
}

//사용은 이렇게!
SpellChecker.INSTANCE.isValid(word);

 

 

두 방법 모두 확장에 유연하지 않고 테스트가 어렵다.

사전은 굉장히 여러 종류가 있는데(한국어 사전, 영어 사전, 특수 어휘용 사전 등...)
dictionary 하나로만 이 역할을 모두 수행하기에는 어렵고,
SpellChecker는 dictionary 하나만 사용할 수 있기 때문이다.

사용하는 자원에 따라 동작이 달라지는 클래스는 위 두 방법이 적합하지 않다.

 

그렇다면 아래와 같이 final을 삭제하고 사전을 교체하는 메소드를 작성하는 것은 어떨까?

public class SpellChecker {
    private static Lexicon dictionary = ...;
    
    ...
    public static void changeDictionary(Lexicon newDictionary) {
    	dictionary = newDictionary;
    }
    ...
}

//사용은 이렇게!
SpellChecker.changeDictionary(newDictionary);

-> 어색하고 멀티스레드 환경에서는 사용할 수 없다.

 

 

 

3. 의존 객체 주입의 형태

이 방법은 인스턴스를 생성할 때 생성자에게 필요한 자원을 넘겨주는 방식이다.
코드를 보는게 더 이해하기 편하다.

public class SpellChecker {
    private final Lexicon dictionary;
    
    // 여기서 의존성 주입을!
    public SpellChecker(Lexicon dictionary){
    	this.dictionary = Objects.requireNotNull(dictionary);
    }
    
    public static boolean isVaild(String word) {...}
    public static List<String> suggestions(String typo) {...}
}

// 인터페이스
interface Lexicon {}

// Lexicon을 상속 받아서 구현
public class myDictionary implements Lexicon {
	...
}

// 사용은 이렇게!
Lexicon dic = new myDictionary();
SpellChecker chk = new SpellChecker(dic);

chk.isVaild(word);

이 패턴의 응용 방법으로는 생성자에 자원 팩토리를 넘겨주는 방식이 있다. (팩토리 메소드 패턴)
ex. Supplier

이처럼 의존성 주입은 유연성과 테스트 용이성을 개선해주지만, 의존성이 너무 많아지면 코드가 장황해질 수도 있다.

 

 

 

https://velog.io/@ajufresh/%EC%95%84%EC%9D%B4%ED%85%9C-5-%EC%9E%90%EC%9B%90%EC%9D%84-%EC%A7%81%EC%A0%91-%EB%AA%85%EC%8B%9C%ED%95%98%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%9D%98%EC%A1%B4-%EA%B0%9D%EC%B2%B4-%EC%A3%BC%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC-rv280ch8