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

박상준·2024년 5월 22일
0

이펙티브 자바

목록 보기
5/16

개요

  • 클래스가 하나 이상의 자원에 의존하는 경우, 자원의 직접 명시 말고 의존 객체 주입(Dependency Injection, DI ) 를 사용하라고한다.

잘못된 구현 방식

import java.util.List;

public class SpellChecker {
    private static final Lexicon dictionary = new Lexicon();
    //    private Lexicon lexicon;

    private SpellChecker() {

    } // 객체 생성 방지

    public static boolean isValid(String word) {

        return false;
    }

    public static List<String> suggestions(String typo) {

        return null;
    }
}
  • static final 같은 정적 유틸리티로 설정하는 경우, 유연하지 않고 테스트하기가 어려워진다.

문제 해결을 위한 고찰

  1. 필드에서 final 제거
    • final 한정자를 제거하고, 다른 사전으로 교체하는 메서드를 추가할 수 도 있다,
    • 하지만, 해당 방식의 경우 어색하기도하고, 오류를 내기 쉽다는 문제가 있다.
    • 또한 멀티 스레드 환경에서는 사용하기가 어렵다.
  2. 의존성 주입 패턴
    • 의존객체 주입을 통하여 필요한 자원을 외부에서 주입받아 유연성, 테스트 용이성을 높일 수 있다.

의존성 주입

  • 클래스가 여러 자원 인스턴스를 지원하고, 클라이언트가 원하는 자원을 사용하도록 한다.
  • 이를 위하여 인스턴스를 생성할 때, 생성자에 필요한 자원을 넘겨주는 방식을 선택한다.
public class SpellChecker {
    private final Lexicon dictionary;

    public SpellChecker(Lexicon dictionary) {
        this.dictionary = Objects.requireNonNull(dictionary);
    }

    public boolean isValid(String word) {

        return false;
    }

    public List<String> suggestions(String typo) {

        return null;
    }
}
  • Lexicon이 필요한 경우 주입하고,
  • 추가적인 사전이 필요한 경우 생성자에 추가만 해주면 된다.
  • 결합도가 낮아진다.
  • 테스트용 자원 주입이 가능하다.
    • Mocking 이 가능해진다.

팩토리 메서드 패턴

  • 생성자에 자원 팩토리를 넘겨주는 방식도 유용
  • 자바 8 에서 Supplier 인터페이스를 사용한다.
public class Mosaic {
    public Mosaic create(Supplier<? extends Tile> tileFactory) {
    }
}

의존 객체를 주입하는 프레임워크 들..

  • 의존성이 많은 큰 프로젝트는 코드의 가독성이 현저히 떨어질 가능성이 있다.
  • 그래서 대거, 주스, 스프링 같은 의존 객체 주입 프레임워크가 등장했다고 한다.

정리

  • 클래스가 내부적으로 하나 이상의 자원에 의존, 해당 자원이 클래스의 동작에 영향을 준다면, 싱글턴 - 정적 유틸리티 클래스는 사용하지 않는 것이 좋다.
    • 근데 정적 유틸리티 클래스의 경우 무조건 사용하지 말라는 것이 아니라, 클래스가 DB 연결, 파일이나 네트워크 리소스 등에 의존하고, 해당 자원이 클래스의 동작에 영향을 미치는 경우에는 사용하지 말라는 것이다.
    • 일반적으로 검증용 정적 메서드나, 유틸성 클래스가 무조건 금지되는 것은 아니다. 해당 클래스들의 경우 어떠한 의존도 가지지 않고, 순순하게 메서드 자체가 그 기능을 다 하기에 정의해도 되는 것이다.
      public class MathUtils {
          private MathUtils() {} // 객체 생성 방지
      
          public static int add(int a, int b) {
              return a + b;
          }
      }
      • 같이 상태를 가지지 않는 유틸리티 메서드는 허용된다.

      • 다만!

        public class SpellChecker {
            private static final Lexicon dictionary = ...;
        
            private SpellChecker() {} // 객체 생성 방지
        
            public static boolean isValid(String word) { ... }
            public static List<String> suggestions(String typo) { ... }
        }
      • 같이 Lexicon 이라는 외부 클래스에 의존하는 클래스는 절대 static 을 사용하지 말자.

  • 필요한 자원을 생성자에 넘겨주는 의존 객체 주입 기법의 경우 클래스의 유연성, 재사용성, 테스트 용이성을 크게 개선할 수 있다.
profile
이전 블로그 : https://oth3410.tistory.com/

0개의 댓글