if 없는 분기처리(feat. interface)

Bobby·2023년 2월 8일
0

즐거운 개발일지

목록 보기
18/22
post-thumbnail

개발을 하다보면 타입에 따라 다른 동작을 해야 할 때가 많이 생긴다.
물론 간단하다면 if문으로도 충분한 경우도 많다.
확장 가능성이 있고 복잡한 로직일 경우 자주 사용하는 방법이다.


💵 결제 시스템

  • 비슷한 상황에서 리팩토링 했던 경험을 기반으로 간단하게 작성했다.

결제 프로세스가 있다고 해보자.
결제 종류는 마일리지 결제, 원화 결제가 있다.

리팩토링 전

public enum PaymentType {

    WON, MILEAGE
}

❕ 결제 종류 별로 결제 처리를 하기 위해서 if 또는 switch 사용할 수 있다.

public void payment(PaymentType paymentType) {

    if (paymentType.equals(PaymentType.WON)) {
        // TODO 결제 프로세스
        log.info("원화 결제 완료!");
    } else if (paymentType.equals(PaymentType.MILEAGE)) {
        // TODO 결제 프로세스
        log.info("마일리지 결제 완료!");
    }
}

❓ 여기서 달러 결제 방식이 추가 된다면 어떻게 될까?

public enum PaymentType {

    WON, MILEAGE, DOLLAR // 추가!
}
public void payment(PaymentType paymentType) {

    if (paymentType.equals(PaymentType.WON)) {
        // TODO 결제 프로세스
        log.info("원화 결제 완료!");
    } else if (paymentType.equals(PaymentType.MILEAGE)) {
        // TODO 결제 프로세스
        log.info("마일리지 결제 완료!");
    } else if (paymentType.equals(PaymentType.DOLLAR)) { // 추가!
        // TODO 결제 프로세스
        log.info("달러 결제 완료!");
    }
}

❕ 타입이 추가되면서 클라이언트 코드도 수정이 되었다.
❕ 타입이 늘어나면 늘어날수록 가독성도 나빠지고 실수할 여지가 생길 것이다.

리팩토링

❕ 인터페이스를 활용하여 리팩토링 해보자.
❕ 스프링을 이용해 빈으로 등록하고 사용했다.

결제 처리를 담당하는 클래스를 만들기 위해 인터페이스를 하나 만든다.

  • PaymentProcessor.class
메소드설명
isSupported()현재 객체가 처리할 수 있는 타입인지 체크
payment()결제!!
public interface PaymentProcessor {

    boolean isSupported(PaymentType paymentType);

    void payment();
}

원화 결제 처리를 담당하는 클래스

  • WonPaymentProcessor.class
@Slf4j
@Component
public class WonPaymentProcessor implements PaymentProcessor {
    @Override
    public boolean isSupported(PaymentType paymentType) {
        return paymentType.equals(PaymentType.WON);
    }

    @Override
    public void payment() {
        log.info("원화 결제 완료!");
    }
}

마일리지 결제 처리를 담당하는 클래스

  • MileagePaymentProcessor.class
@Slf4j
@Component
public class MileagePaymentProcessor implements PaymentProcessor {
    @Override
    public boolean isSupported(PaymentType paymentType) {
        return paymentType.equals(PaymentType.MILEAGE);
    }

    @Override
    public void payment() {
        log.info("마일리지 결제 완료!");
    }
}

결제를 담당하는 클래스

❕ 생성한 각 클래스들은 빈으로 생성기 때문에 상위 인터페이스의 리스트로 주입 받을 수 있다.
❕ 이 클래스는 매우 단순하다. 처리할 수 있는 결제 프로세서를 찾아 실행한다.

  • PaymentProcess.class
@Slf4j
@Component
@RequiredArgsConstructor
public class PaymentProcess {

    private final List<PaymentProcessor> paymentProcessors; // 빈 주입

    public void payment(PaymentType paymentType) {

        PaymentProcessor paymentProcessor = paymentProcessors.stream()
                .filter(p -> p.isSupported(paymentType))
                .findFirst()
                .orElseThrow(); // 결제 프로세서 조회

        paymentProcessor.payment(); // 결제
    }
}

❗️ 실행

@SpringBootTest
class PaymentProcessTest {

    @Autowired
    PaymentProcess paymentProcess;

    @Test
    void payment() {
        paymentProcess.payment(PaymentType.WON);
        paymentProcess.payment(PaymentType.MILEAGE);
    }
}

❓ 여기서 달러 결제가 생긴다면?

❕ 달러 결제를 담당하는 클래스만 추가해주면 끝!

  • DollarPaymentProcessor.class
@Slf4j
@Component
public class DollarPaymentProcessor implements PaymentProcessor {
    @Override
    public boolean isSupported(PaymentType paymentType) {
        return paymentType.equals(PaymentType.DOLLAR);
    }

    @Override
    public void payment() {
        log.info("달러 결제 완료!");
    }
}

참 쉽죠..?


전체코드


아~ 리팩토링 마렵다~

profile
물흐르듯 개발하다 대박나기

0개의 댓글