많은 클래스는 하나 이상의 자원에 의존한다. 이 책에서는 맞춤법 검사기인 SpellChecker
와 dictionary
를 예로 들고있다. 즉, SpellChecker
가 dictionary
를 사용하고, 이를 의존 하는 리소스 또는 의존성이라고 부른다. 이때 SpellChecker
를 다음과 같이 구현하는 경우가 있다.
부적절한 구현
static 유틸 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class SpellChecker {
//한국 사전으로 고정되어있음
private static final Lexicon dictionary = new KoreanDictionary();
private SpellChecker(){
// 객체생성방지
}
public static boolean isValid(String word){
throw new UnsupportedOperationException();
}
public static List<String> suggestions(String typo){
throw new UnsupportedOperationException();
}
public static void main(String[] args) {
SpellChecker.isValid("hello");
}
}
interface Lexicon{}
class KoreanDictionary implements Lexicon{}
위소스 내 모든 유틸리티 메소드는 public한 static으로 만들어져 있다. 이 메소드들에서 dictionary을 참조해야 하므로 생성자도 static 타입으로 생성해야 한다. 생성자는 new koreanDictionary()
을 통해 만들어 final로 지정했기 때문에 고정이 되어 변경하기가 힘들다.
SpellChecker 클래스를 테스트 할 때도 dictionary까지 테스트하게된다. 이렇게 되면 유연하지 않고 테스트하기 어렵다는 단점이 있다.
싱글턴 클래스(정적 팩터리 방식)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class SpellChecker {
//한국 사전으로 고정되어있음
private final Lexicon dictionary = new KoreanDictionary();
private SpellChecker(){
// 객체생성방지
}
public static final SpellChecker INSTANCE = new SpellChecker(){
};
public static boolean isValid(String word){
throw new UnsupportedOperationException();
}
public static List<String> suggestions(String typo){
throw new UnsupportedOperationException();
}
public static void main(String[] args) {
SpellChecker.INSTANCE.isValid("hello");
}
}
interface Lexicon{}
class KoreanDictionary implements Lexicon{}
사전을 하나만 사용할거라면 위와 같은 구현도 만족스러울 수 있겠지만, 실제로는 각 언어의 맞춤법 검사기는 사용하는 사전이 각기 다르다. 또한 테스트 코드에서는 테스트용 사전을 사용하고 싶을 수도 있다.
위 소스코드가 부적절하고 유연하지 않은 이유는 언어가 바뀌면 위 소스코드에서 new KoreanDictionary()
이 부분이 바뀌어야 하기 때문이다.
어떤 클래스가 사용하는 자원에 따라 동작이 달라지는 클래스에는 스태틱 유틸리티 클래스와 싱글톤을 사용하는 것은 부적절하다.
이런 경우 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식이 낫다. 이는 의존 객체 주입의 한 형태로, 맞춤법 검사기를 생성할 때 의존객체인 사전을 주입해주면 된다.
적절한 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class SpellChecker {
private Lexicon dictionary;
//생성자에 의존객체인 사전을 주입
public SpellChecker(Lexicon dictionary){
this.dictionary = Objects.requireNonNull(dictionary);
};
public static boolean isValid(String word){
throw new UnsupportedOperationException();
}
public static List<String> suggestions(String typo){
throw new UnsupportedOperationException();
}
public static void main(String[] args) {
}
}
interface Lexicon{}
class KoreanDictionary implements Lexicon{}