Map
이나 List
형태로 전체 구현체 bean을 주입받고전략 패턴은 자바의 다형성을 활용하는 디자인 패턴이다. 이번 케이스에서 구현체를 선정한다는 것은 if나 case문을 통한 분기가 아니라 map의 key를 입력받아서 이용하기 때문에 소스를 수정하지 않고도 구현체만 계속해서 확장할 수 있다.
전략패턴 참고 👉 https://velog.io/@jaycee/전략-패턴-Strategy-Pattern
String 형태로 bean 이름을 입력해줄 수도 있기 때문에 컴파일 단계에서 bean 이름을 잘못 선택했는지는 확인이 불가능하다. 타입을 잘못 입력하는 경우 사전에 컴파일 에러를 내고 싶다면 enum을 이용할 수도 있다.
스프링 컨테이너는 스프링 bean을 bean의 추상화 객체가 아닌 Map<SomeInterfaceBean>
과 같은 추상화 객체를 담고 있는 Map
이나 List
로 받게 되면, 컨테이너에 등록된 모든 SomeInterfaceBean
타입의 bean들이 주입된다.
스프링 bean들을 컬렉션 형태로 주입을 받을 수 있고 주로
Map
이나List
형태를 사용한다.
일반적인 형태 - 스프링 컨테이너에서 DiscountPolicy bean 하나만 찾아서 주입
public class OrderServiceImpl(){
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(DiscountPolicy discountPolicy){
this.discountPolicy = discountPolicy;
}
}
DiscountPolicy 타입으로 등록된 전체 bean을 찾아 disCountPolicyMap에 주입 하는 형태
public class OrderServiceImpl(){
private final Map<String, DiscountPolicy> disCountPolicyMap;
public OrderServiceImpl(Map<DiscountPolicy> discountPolicyMap){
this.discountPolicyMap = discountPolicyMap;
}
}
다형성을 이용한 처리는 아래와 같은 케이스가 있다.
package hello.core.autowired;
import hello.core.AutoAppConfig;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Grade;
import hello.core.member.Member;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
public class AllBeanTest {
@Test
void findAllBean(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int fixDiscountPrice = discountService.discount(member, 10000, "fixDiscountPolicy");
assertThat(fixDiscountPrice).isEqualTo(1000); //반환되는 할인 가격이 1000원인지 확인.
int rateDiscountPrice = discountService.discount(member, 20000, "rateDiscountPolicy");
assertThat(rateDiscountPrice).isEqualTo(2000); //반환되는 할인 가격이 2000원인지 확인.
}
@Component
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
@Autowired
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
System.out.println("policyMap = " + policyMap); //DiscountPolicy의 모든 구현체가 Map에 주입
System.out.println("policies = " + policies);//DiscountPolicy의 모든 구현체가 List에 주입
}
public int discount(Member member, int price, String discountCode){
DiscountPolicy discountPolicy = policyMap.get(discountCode);
return discountPolicy.discount(member, price);
}
}
}