인프런 커뮤니티 질문&답변

dudwns328님의 프로필 이미지
dudwns328

작성한 질문수

스프링 핵심 원리 - 기본편

Request에 따라 다른 bean을 선택하는 법

작성

·

243

0

[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예

[질문 내용]
OCP관점에서 컨트롤러 설계에 관해 궁금한 것이 있습니다.

@Controller
public class PaymentController {
    @PostMapping("/payment")
    public void pay(@RequestBody PaymentRequest req) {
        CardPaymentService cardPaymentService = cardPaymentFactory.getType(req.getType());
        cardPaymentService.pay(req);
    }
}

@Service public class APaymentService implements CardPaymentService {...}
@Service public class BPaymentService implements CardPaymentService {...}


public class CardPaymentFactory {
    ...
    public CardPaymentService getType(CardType type) {
        CardPaymentService cardPaymentService;
        switch (type) {
            case A:
                cardPaymentService = aCardPaymentService;
                break;
            case B:
                cardPaymentService = bCardPaymentService;
                break;
            default:
                throw new IllegalArgumentException();
        }
        return cardPaymentService;
    }
}

 

위와 같은 구조가 있다고 할 때

저런식으로 switch-case 문으로 설계하면 OCP 원칙에 위배된다고 생각하여 아래 내용이 궁금합니다.

CardPaymentService cardPaymentService = cardPaymentFactory.getType(req.getType());

(위 코드를 없애는 방법이 궁금합니다)

  1. Factory class 없이 Controller에서 req에 따라 자동으로 맞는 service가 주입하는 방법은 없는지?

     

  2. 설계를 다르게 해야하는지?

답변 1

1

안녕하세요, dudwns328 님. 공식 서포터즈 y2gcoder 입니다.

CardPaymentService 인터페이스 구현체들을 각각 @Service를 이용해 빈으로 등록했고, PaymentController에서 req를 통해 들어오는 type에 따라 서비스의 구현체를 사용하고 싶고, 이를 좀 더 객체지향적으로 만들고 싶으신 것으로 이해했습니다.

제가 dudwns328님의 코드에 있는 인터페이스 코드를 모르고 요구사항을 명확하게 모르는 상황에서 드리는 답변이기 때문에, 조금 이상한 부분이 있더라도 너그럽게 넘어가주시면 감사하겠습니다 :)

인터페이스의 구현체들을 빈으로 등록했기 때문에 우리는 이 인터페이스 타입을 이용해볼 수 있을 것 같습니다.

public interface CardPaymentService {
    boolean supportsType(Type type);
    void pay(PaymentRequest req);
}

먼저 이런 식으로 인터페이스를 만들고,

@Controller
@RequiredArgsConstructor
public class PaymentController {
    private final List<CardPaymentService> cardPaymentServices;

    @PostMapping("/payment")
    public void pay(@RequestBody PaymentRequest req) {
        for (CardPaymentService service : cardPaymentServices) {
            if (cardPaymentService.supportsType(req.getType())) {
                cardPaymentService.pay(req);
            }
        }
    }
}

이런 식으로 인터페이스 타입의 리스트로 모든 빈을 가져와서 빈 리스트를 순회하면서 해당 타입을 지원하는 서비스의 pay()를 실행할 수 있을 것 같습니다.

이렇게 하면 새로운 서비스를 추가할 때 다른 코드를 수정할 필요 없이 CardPaymentService를 구현하여 만들면 되기 때문에 괜찮지 않을까 하여 추천합니다.



감사합니다.

dudwns328님의 프로필 이미지
dudwns328
질문자

답변 감사합니다 제가 궁금했던 것이었습니다!

혹시 supportsType 메서드말고 서비스별로 필드에 enum형태의 타입을 지정해줘서 이거를 비교하는 것은 어떨까요? 근데 이렇게 하면 인터페이스를 구체화할 때 필드 선언을 강제할 수 없어서 고민이네요

혹시 supportsType 메서드말고 서비스별로 필드에 enum형태의 타입을 지정해줘서 이거를 비교하는 것은 어떨까요?

해보시는 것을 추천드립니다! 그 후 결과물을 공유해주시면 저나 다른 분들께도 큰 도움이 될 것 같습니다 :)

파이팅입니다!

dudwns328님의 프로필 이미지
dudwns328

작성한 질문수

질문하기