이펙티브 자바 완벽 공략 1부

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

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

핵심 정리

  • 사용하는 자원에 따라 동작이 달라지는 클래스는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다
  • 의존 객체 주입이란 인스턴스를 생성할 때 필요한 자원을 넘겨주는 방식이다
  • 이 방식의 변형으로 생성자에 자원 팩터리를 넘겨줄 수 있다.
  • 의존 객체 주입을 사용하면 클래스의 유연성, 재사용성, 테스트 용이성을 개선할 수 있다

public class SpellChecker {

    private static final Dictionary dictionary = new DefaultDictionary();

    private SpellChecker() {}

    public static boolean isValid(String word) {
        // TODO 여기 SpellChecker 코드
        return dictionary.contains(word);
    }

    public static List<String> suggestions(String typo) {
        // TODO 여기 SpellChecker 코드
        return dictionary.closeWordsTo(typo);
    }
}

  • 코드를 보면 딕셔너리를 바꿔 넣을 수가 없기 때문에 이 딕셔너리를 반드시 꼭 사용해야 한다
  • 그리고 이 딕셔너리를 만드는데 많은 비용이 든다고 가정을 해보면 CPU도 많이 쓰고 메모리도 많이 써야 한다 그럼 테스트를 만드는 과정이 굉장히 비효율적이다 이 코드에 대한 테스트를 만드려면 이렇게 만들 수밖에 없을 것이다
  • 싱글톤으로 작성하더라도 만약에 본인이 사용하는 리소스를 이 안에 이렇게 직접 정의하면 동일한 문제가 생긴다 딕셔너리 없이는 테스트가 불가능하다 그리고 유연성이 떨어지고 재사용성도 떨어진다
    • 예를들어 만약에 딕셔너리를 영어 사전이 아니라 한국어 사전을 서포트하고 싶을 경우

public class DictionaryFactory {
    public static DefaultDictionary get() {
        return new DefaultDictionary();
    }
}



public class SpellChecker {

    private final Dictionary dictionary;

    public SpellChecker(Dictionary dictionary) {
        this.dictionary = dictionary;
    }

    public SpellChecker(Supplier<Dictionary> dictionarySupplier) {
        this.dictionary = dictionarySupplier.get();
    }

    public boolean isValid(String word) {
        // TODO 여기 SpellChecker 코드
        return dictionary.contains(word);
    }

    public List<String> suggestions(String typo) {
        // TODO 여기 SpellChecker 코드
        return dictionary.closeWordsTo(typo);
    }
}


  • 만약에 우리가 클래스를 의존성 주입을 사용해서 만든다면 이렇게 어떤 리소스에 따라 달라지는 코드를 의존성 주입으로 이렇게 의존성을 주입받을 수 있는 생성자로 만들면 모든 코드를 다 재사용 가능하다
    • 물론 전제가 하나 더 존재한다 딕셔너리가 인터페이스였을 때 이 모든 코드는 재사용이 가능하다
  • 이렇게 만들어주면 이 코드를 테스트하는 것도 굉장히 유연해진다

완벽 공략

  • p29, 이 패턴의 쓸만한 변형으로 생성자에 자원 팩터리를 넘겨주는 방식이 있다
  • p29, 자바 8에서 소개한 Supplier 인터페이스가 팩터리를 표현한 완벽한 예다

    public SpellChecker(Supplier<Dictionary> dictionarySupplier) {
        this.dictionary = dictionarySupplier.get();
    }

  • p29, 한정적 와일드카드 타입을 사용해 팩터리의 타입 매개변수를 제한해야 한다

    public SpellChecker(Supplier<? extends Dictionary> dictionarySupplier) {
        this.dictionary = dictionarySupplier.get();
    }

  • p29, 팩터리 메소드 패턴
  • p30, 의존 객체가 많은 경우에 Dagger, Guice, 스프링 같은 의존 객체 주입 프레임워크 도입을 고려할 수 있다

아이템 5. 완벽 공략 14 - 팩터리 메서드 패턴

구체적으로 어떤 인스턴스를 만들지는 서브 클래스가 정한다.

  • 새로운 Product를 제공하는 팩토리를 추가하더라도, 팩토리를 사용하는 클라이언트 코드는 변경할 필요가 없다
  • Item5-14.png
  • Factory를 사용해 가지고 만드는 과정 중에 비슷한 부분을 재사용을 하고 또 다른 부분은 제각각 다른 공정을 거쳐서 만들도록 한다
  • Factory Method Pattern은 구체적인 인스턴스를 구체적인 Factory Method에서 만들어주는 패턴이다
  • 새로운 프로덕트를 추가할 수 있고 새로운 팩토리를 만들 수도 있다 클라이언트 코드에 아무런 변경을 가하지 않고

public interface DictionaryFactory {

    Dictionary getDictionary();

}


public class DefaultDictionaryFactory implements DictionaryFactory {
    @Override
    public Dictionary getDictionary() {
        return new DefaultDictionary();
    }
}


public class MockDictionaryFactory implements DictionaryFactory {
    @Override
    public Dictionary getDictionary() {
        return new MockDictionary();
    }
}



public class SpellChecker {

    private Dictionary dictionary;

    public SpellChecker(DictionaryFactory dictionaryFactory) {
        this.dictionary = dictionaryFactory.getDictionary();
    }

    public boolean isValid(String word) {
        // TODO 여기 SpellChecker 코드
        return dictionary.contains(word);
    }

    public List<String> suggestions(String typo) {
        // TODO 여기 SpellChecker 코드
        return dictionary.closeWordsTo(typo);
    }

}

아이템 5. 완벽 공략 15 - 스프링 IoC

BeanFactory 또는 ApplicationContext

  • Inversion of Control - 뒤짚힌 제어권
    • 자기 코드에 대한 제어권을 자기 자신이 가지고 있지 않고 위부에서 제어하는 경우
    • 제어권? 인스턴스를 만들거나, 어떤 메소드를 실행하거나, 필요로하는 의존성을 주입 받는 등
  • 스프링 IoC 컨테이너 사용 장점
    • 수많은 개발자에게 검증되었으며 자바 표준 스팩(@Inject)도 지원한다
    • 손쉽게 싱글톤 Scope을 사용할 수 있다
    • 객체 생성 (Bean) 관련 라이프사이클 인터페이스를 제공한다

© 2020. All rights reserved.

SIKSIK