스프링에서 전략패턴 사용하기

jaycee·2023년 2월 13일
0

스프링에서 전략패턴을 사용하는 방법

  1. 의존성을 주입 받을 때 Map이나 List형태로 전체 구현체 bean을 주입받고
  2. 호출하는 쪽의 파라미터를 통해 1번에서 주입받은 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;
	}
}

전략패턴을 사용한 예

다형성을 이용한 처리는 아래와 같은 케이스가 있다.

  • 할인 정책 (정액, 정률, 무료)중 사용자 요청에 따라 택1
  • 네이버 쇼핑, 카카오 쇼핑에서 받는 주문 데이터 처리
  • 위 케이스에서 일부는 같은 동작(동일한 데이터베이스에 입력)을 하고, 일부는 다른 동작(인증, 응답 데이터 처리)를 하는 형태의 구현이 필요할 때

예제 소스

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);
        }
    }
}
profile
오늘도 하나 배웠다.

0개의 댓글