본문 바로가기

도서/이펙티브 자바

[이펙티브 자바] 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

 

여러 자원에 의존하고, 자원에 의해 동작이 달라지는 클래스는 의존 객체 주입을 사용하자

 

정적 유틸리티, 싱글턴 방식

다른 자원에 의존하는 클래스가 있을 때, 클래스 내부에서 이를 생성하여 (명시)하는 경우가 많다.

정적 유틸리티로 구현하거나, 싱글턴으로 구현하는 경우 의존 클래스를 하나만 사용한다는 가정이 있어야한다.

 

하지만 실제로는 의존 클래스가 여러 용도에 따라 다르게 필요할 수 있어, 유연성이 떨어지고 테스트하기도 어렵다.

즉, 사용 자원에 따라 동작이 달라지는 클래스의 경우 적합하지 않다. 

// 정적 유틸리티
public class SpellChecker {
	private static final Lexicon dictionary = ...;
    
    private SpellChecker() {} // 객체 생성 방지용 private 생성자
    
    public static boolean isValid(String word) { ... }
    public static List<String> suggestions(String typo) { ... }
}

// 싱글턴
public class SpellChecker {
	private static final Lexicon dictionary = ...;
    
    private SpellChecker() {} // 객체 생성 방지용 private 생성자
    public static SpellChecker INSTANCE = new SpellChecker(...);
    
    public boolean isValid(String word) { ... }
    public List<String> suggestions(String typo) { ... }
}

 

 

의존 객체 주입 방식  (Dependency Injection, DI)

인스턴스를 생성할 때 생성자에 필요한 지원을 넘겨주는 방식, 의존 객체를 유연하게 처리할 수 있다.

  • 의존 자원의 개수, 관계와 상관 없이 작동
  • 생성자뿐 아니라 정적 팩터리, 빌더 등에 응용 가능 
  • 의존이 달라져도 SpellChecker 코드 수정할 필요 없음
public class SpellChecker{
    private final Lexicon dictionary; // final 필드로 받음, 불변
   
    // 의존 객체 dictionary를 주입 받음
    public SpellChecker(Lexicon dictionary){
    	this.dictionaryy = Objects.requireNonNull(dictionary);
    }
    
    public boolean isValid(String word) { ... }
    public List<String> suggestions(String typo) { ... };
 }

 

 

의존 객체 주입 변형 - 자원 팩터리 

팩터리 - 인스턴스를 생성하는 객체

기존 의존 객체 주입에서는 객체를 직접 주입했었는데, 그 대신 팩터리를 주입하는 방식

 

  • 매번 새로운 객체를 동적으로 주입할 수 있음
  • Mock 객체를 쉽게 주입 가능
// 팩터리
@FunctionalInterface
public interface LexcionFactory {
	Lexcion create();
}

// 팩터리 주입 방식
public class SpellChecker {
    private final LexiconFactory lexiconFactory;

    public SpellChecker(LexiconFactory lexiconFactory) {
        this.lexiconFactory = Objects.requireNonNull(lexiconFactory);
    }

    public void checkSpelling(String word) {
        Lexicon lexicon = lexiconFactory.create(); // 필요할 때마다 새로운 Lexicon 생성
    }
}

// 실제 사용 
// 한국어
SpellChecker koreanSpellChecker = new SpellChecker(KoreanDictionary::new);

// 영어
SpellChecker englishSpellChecker = new SpellChecker(EnglishDictionary::new);

 

반응형