본문 바로가기

TIL

[2022-1-10]스프링 핵심원리 - 기본편 : 스프링 컨테이너와 스프링 빈 (2/2) / 프로그래머스 코딩테스트 연습문제 - 실패율

스프링 핵심원리 - 기본편(스프링 컨테이너와 스프링 빈 2/2)

부모 타입으로 조회

컨테이너에서 특정 Bean을 조회할 때, 상속관계에 대해서 조회하려고 하는 Bean과 이 객체를 상속받는 모 든 Bean을 동시에 조회한다. 아래 코드는 테스트를 위해 만든 TestConfig 클래스이다.

@Configuration
static class TestConfig {
    @Bean
    public DiscountPolicy rateDiscountPolicy() {
        return new RateDiscountPolicy();
    }

    @Bean
    public DiscountPolicy fixDiscountPolicy() {
        return new FixDiscountPolicy();
    }
}

이 TestConfig를 통해 컨테이너를 생성하면 DiscountPolicy를 상속받는 구현 객체 RateDiscountPolicy와 FixDiscountPolicy가 각각 Bean으로 등록된다.

 

이에 대해 ac.getBean() 메소드를 통해 DiscountPolicy 타입으로 조회를 시도하면NoUniqueBeanDefinitionException 이 발생한다. 아래는 이에대한 테스트 코드이다.

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

@Test
@DisplayName("부모 타입으로 조회시 자식이 둘 이상 있으면 중복 오류가 발생한다.")
void findBeanByParentTypeDuplicate(){
    Assertions.assertThrows(NoUniqueBeanDefinitionException.class,()-> ac.getBean(DiscountPolicy.class));
}

 

이러한 중복 빈 오류에 대해, 빈 이름을 통해 Bean을 조회하므로써 오류를 피할 수 있다. 

@Test
@DisplayName("부모 타입으로 조회시 자식이 둘 이상 있으면 빈이름을 지정한다.")
void findBeanByParentTypeBeanName(){
    DiscountPolicy fixDiscountPolicy = ac.getBean("fixDiscountPolicy", DiscountPolicy.class);
    assertThat(fixDiscountPolicy).isInstanceOf(FixDiscountPolicy.class);
}

 

아래는 빈 이름이 아닌 구현 클래스를 통해 Bean을 조회하는 코드이다.

@Test
@DisplayName("특정 회원타입으로 조회")
void findBeanBySubType(){
    DiscountPolicy fixDiscountPolicy = ac.getBean(FixDiscountPolicy.class);
    assertThat(fixDiscountPolicy).isInstanceOf(FixDiscountPolicy.class);
}

하지만 이런 코드는 구현객체에 직접적으로 의존하게 되므로  이런 방식은 지양해야한다.

 

이러한 Bean 의 성질 때문에 자바의 모든 객체의 부모인 Object를 통해 Bean을 조회하면, 아래와 같이 컨테이너에 등록된 모든 Bean 을 조회할 수 있다.

@Test
@DisplayName("부모타입으로 모두 조회")
void findAllBeanByObjectType(){
    Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
    for (String key : beansOfType.keySet()) {
        System.out.println("key = " + key + " value = " + beansOfType.get(key));
    }
}

프로그래머스 코딩테스트 연습문제 - 실패율

오늘 푼 문제는 실패율 이었다.

import java.util.*;

class Solution {
    public int[] solution(int N, int[] stages) {
        Map<Integer,Double> stageFailRates = new HashMap<>();
        int allUser = stages.length;
        int stageUser;
        double failRate;
        for (int stage = 1; stage <= N; stage++) {
            stageUser = getUserNumber(stages,stage);
            failRate = (double)stageUser / allUser;
            stageFailRates.put(stage, failRate);
            allUser -= stageUser;
            allUser = allUser == 0 ? 1 : allUser;
        }

        List<Integer> keySetList = new ArrayList<>(stageFailRates.keySet());
        keySetList.sort((o1, o2) -> stageFailRates.get(o2).compareTo(stageFailRates.get(o1)));
        int[] answer = new int[N];
        for (int i = 0; i < answer.length; i++) {
            answer[i] = keySetList.get(i);
        }
        return answer;
    }

    private int getUserNumber(int[] stages,int findStage){
        int number = 0;
        for (int stage : stages) {
            if(stage == findStage){
                number++;
            }
        }
        return number;
    }
}

이제 문제를 풀면서 코드의 길이에 크게 집중하기보단 왜 이렇게 코드를 짜는지에 대해 생각하면서 풀어보려고 했다.

 

문제자체가 어렵지는 않았지만, 아직 자바의 스트림, 람다 등의 사용이 익숙하지 않은 상태에서 이를 에디터의 도움을 받아 사용하는 것이 찝찝한 한편, 조금 더 시간이 걸리더라도 라이브러리 사용을 자제하고 순수한 내 코드로 문제를 푸는 것과, 자바가 제공하는 라이브러리들을 적극 활용하여 푸는 것 사이에서 고민이 많았다. 결국 이를 판단하기 위해선 라이브러리의 동작을 정확히 이해해야만 그 때 그때 필요한 것을 알맞게 사용할 수 있겠다는 결론에 도달했다. 이번 주가 가기전에 Stream 이 어떻게 동작하고 어떨 때 사용해야하는지 정리해보고 많이 사용해봐야겠다.

결론 및 느낀 점

공부를 하면 할수록 더많은 공부할 것 들이 생기면서, 여기 저기 해매는 시간 때문에 처음 시작했을 때보다 공부가 수월하게 진행 되지 않는 느낌이다. 그래도 매일 블로그에 공부내용을 기록하는 것이, 공부할 수있는 큰 원동력이 되는 것 같다. 앞으로 일주일 뒤에는, 한달뒤에는 그리고 1년 뒤에는 더 퀄리티 있는 글을 쓸 수 있도록 더 고민하고 노력하는 사람이 되어야겠다.