Spring의 전략 패턴 심화 - 특정 인터페이스 구현체 주입

전략 패턴에 대한 내용은 해당 포스팅에서 확인할 수 있다.

요구사항

Pay 라는 인터페이스를 구현하는 구현체들 중 특정 조건 별로 구현체를 얻어오는 전략 패턴 문제를 해결해보자.

내용

Pay 인터페이스 정의 및 Pay 인터페이스 구현체는 다음과 같다.

public interface Pay {
    void payAmount(int amount);
}

@Component
public class KakaoPay implements Pay {
    @Override
    public void payAmount(int amount) { /* ... */ }
}

@Component
public class NaverPay implements Pay {
    @Override
    public void payAmount(int amount) { /* ... */ }
}

@Component
public class TossPay implements Pay {
    @Override
    public void payAmount(int amount) { /* ... */ }
}

임의의 컴포넌트에서 DI 기능을 활용하면 Pay 구현체들을 컬렉션 자료구조로 주입받을 수 있다.

@Component
@RequiredArgsConstructor
public class XComponent {
    private final Set<Pay> payHandlers;
}

여기서 만약 특정 타입에 따라 특정한 Pay 구현체를 선택해 사용하도록 하기 위해서는 구현체의 빈 이름을 정의하고, Map<String, Pay> 타입으로 주입받아 처리할 수 있다.

@Component("kakao")
public class KakaoPay implements Pay {
    @Override
    public void payAmount(int amount) { /* ... */ }
}

@Component("naver")
public class NaverPay implements Pay {
    @Override
    public void payAmount(int amount) { /* ... */ }
}

@Component("toss")
public class TossPay implements Pay {
    @Override
    public void payAmount(int amount) { /* ... */ }
}

@Component
@RequiredArgsConstructor
public class XComponent {
    private final Map<String, Pay> payHandlerMap; // key는 빈 이름 ("kakao", "naver", "toss")
    
    public void doPaying(String type, int amount) {
        Pay payHandler = Optional.ofNullable(payHandlerMap.get(type))
            .orElseThrow(() -> new IllegalArgumentException("No pay handler for type: " + type));
        
        payHandler.payAmount(amount);
    }
}

위와 같은 동작이 가능한 이유는 Spring에서 DI를 할 때 해석기가 Map<String, X>와 같은 타입을 보면 value 타입 (X)의 모든 빈을 모아두고 key 값에는 빈 이름으로 채워준다는 규칙을 가지고 있기 때문이다.