ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
    Java/이펙티브 자바 2022. 11. 1. 13:47

    핵심정리 : 자원을 직접 명시하지말고 의존 객체 주입을 사용하라

    - 사용하는 자원에 따라 동작이 달라지는 클래스는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다

    - 의존 객체 주입이란 인스턴스를 생성할 때 필요한 자원을 넘겨주는 방식이다

    - 이 방식의 변형으로 생성자에 자원 팩터리를 넘겨줄 수 있다

    - 의존 객체 주입을 사용하면 클래스의 유연성, 재사용성, 테스트 용이성을 개선할 수 있다.

    public interface Dictionary {
        boolean contains(String word);
    
        List<String> closeWordsTo(String type);
    }
    public class SpellChecker {
    
    //	private static final Dictionary dictionary = new Dictionary(); // 자원 명시
    
        private final Dictionary dictionary;
    
        public SpellChecker(Dictionary dictionary) {
            this.dictionary = dictionary;
        }
    
        public boolean isValid(String word) {
            return dictionary.contains(word);
        }
    
        public List<String> suggestions(String type) {
            return dictionary.closeWordsTo(type);
        }
    }

        ㆍ자원을 직접 명시한다 : 객체 생성을 해준다.

    class SpellCheckerTest {
        @Test
        void isValid() {
            SpellChecker spellChecker = new SpellChecker(new DefaultDictionary());
            spellChecker.isValid("test");
        }
    }

    완벽공략

    p29, 이 패턴의 쓸만한 변형으로 생성자에 자원 팩터리를 넘겨주는 방식이 있다

    p29, 자바 8에서 소개한 Supplier<T> 인터페이스가 팩터리를 표현한 완벽한 예다.

    public class SpellChecker {
    
        private final Dictionary dictionary;
    	
    	// 1
        public SpellChecker(Dictionary dictionary) {
            this.dictionary = dictionary;
        }
    	// 2
        public SpellChecker(DictionaryFactory dictionaryFactory){
            this.dictionary = dictionaryFactory.get();
        }
    	// 3
        public SpellChecker(Supplier<Dictionary> dictionarySupplier) {
            this.dictionary = dictionarySupplier.get();
        }
        ...
    }
    // 3. supplier를 쓴 경우
    @Test
    void isValid() {
    //    SpellChecker spellChecker = new SpellChecker(() -> new DefaultDictionary());
        SpellChecker spellChecker = new SpellChecker(DefaultDictionary::new);
        spellChecker.isValid("test")
    }

     

    public class DictionaryFactory {
        public static Dictionary get() {
            return new DefaultDictionary();
        }
    }
    // 2. factory 쓴 경우
    @Test
    void isValid() {
    //    SpellChecker spellChecker = new SpellChecker(() -> DictionaryFactory.get());
        SpellChecker spellChecker = new SpellChecker(DictionaryFactory::get);
        spellChecker.isValid("test")
    }

     

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

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

    p29, 팩터리 메소드 패턴

    p30, 의존 객체가 많은 경우에 Dagger, Guice, 스프링 같은 프레임워크 도입을 고려할 수 있다.

     

    완벽공략 14. 팩터리 메소드 패턴

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

    - 새로운 Product를 제공하는 팩토리를 추가하더라도, 팩토리를 사용하는 클라이언트 코드는 변경할 필요가 없다.

    public class SpellChecker {
    
        private Dictionary dictionary;
    
        public SpellChecker(DictionaryFactory dictionaryFactory) {
            this.dictionary = dictionaryFactory.getDictionary();
        }
    
        public boolean isValid(String word) {
            return dictionary.contains(word);
        }
    
        public List<String> suggestions(String type) {
            return dictionary.closeWordsTo(type);
        }
    }
    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();
        }
    }

    - 확장에 열려있고 확장에 닫혀있다. ( OCP )

    완벽공략 15. 스프링IoC : BeanFactory 또는 ApplicationContext

    - Inversion of Control : 뒤짚힌 제어권

        ㆍ자기 코드에 대한 제어권을 자기 자신이 가지고 있지 않고 외부에서 제어하는 경우

        ㆍ제어권? 인스턴스를 만들거나, 어떤 메소드를 실행하거나, 필요로 하는 의존성을 주입 받는 등

    - 스프링 IoC 컨테이너 사용 장점

        ㆍ수많은 개발자에게 검증되었으며 자바 표준 스팩(@Inject)도 지원한다

        ㆍ손쉽게 싱글톤 Scope을 사용할 수 있다

        ㆍ객체 생성 (Bean) 관련 라이프사이클 인터페이스를 제공한다.

            ○ ex) Spring AOP

    public class App {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
            SpellChecker spellChecker = applicationContext.getBean(SpellChecker.class);
            spellChecker.isValid("test");
        }
    }
    @Configuration
    @ComponentScan(basePackageClasses = AppConfig.class)
    public class AppConfig {
    //
    //    @Bean
    //    public SpellChecker spellChecker(Dictionary dictionary) {
    //        return new SpellChecker(dictionary);
    //    }
    //
    //    @Bean
    //    public Dictionary dictionary() {
    //        return new SpringDictionary();
    //    }
    }
    @Component
    public class SpellChecker {
    
        private Dictionary dictionary;
    
        public SpellChecker(Dictionary dictionary) {
            this.dictionary = dictionary;
        }
    
        public boolean isValid(String word) {
            return dictionary.contains(word);
        }
    
        public List<String> suggestions(String type) {
            return dictionary.closeWordsTo(type);
        }
    }
    @Component
    public class SpringDictionary implements Dictionary {
        @Override
        public boolean contains(String word) {
            System.out.println("contains " + word);
            return false;
        }
    
        @Override
        public List<String> closeWordsTo(String type) {
            return null;
        }
    }

     

    * POJO ( Plain Of Java Object )

    - 스프링을 쓴다고 해서 인터페이스를 구현해야 한다거나 클래스를 상속받아야 한다던가 (침투적 프레임워크) 할 필요가 없다. (비침투적 프레임워크)

    댓글

Designed by Tistory.