개발을 하다보면 타입에 따라 다른 동작을 해야 할 때가 많이 생긴다.
물론 간단하다면 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("달러 결제 완료!");
}
}
참 쉽죠..?
아~ 리팩토링 마렵다~