If-else 문은 흔하지만 남용되면 복잡하고 유지보수가 어려운 코드로 이어질 수 있습니다. 이 글에서는 Java Spring Boot 프로젝트에서 if-else 문을 줄이기 위한 다양한 전략을 탐구하여 코드를 더 모듈화하고, 유지보수하기 쉽고, 읽기 쉽게 만드는 방법에 대해 설명하겠습니다.
If-Else 문을 줄이는 전략
- 전략 패턴(Strategy Pattern)
- Enum 사용
- 다형성(Polymorphism)
- 람다 표현식과 함수형 인터페이스
- 명령 패턴(Command Pattern)
- 가드 절(Guard Clauses)
1. 전략 패턴
예제: 결제 처리 시스템
먼저 PaymentStrategy 인터페이스를 정의합니다:
public interface PaymentStrategy {
void pay(double amount);
}
그런 다음, 다른 결제 전략을 구현합니다:
@Component
public class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
// 신용 카드 결제 처리 로직
System.out.println("Paid " + amount + " using Credit Card.");
}
}
@Component
public class PaypalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
// PayPal 결제 처리 로직
System.out.println("Paid " + amount + " using PayPal.");
}
}
전략을 사용하는 PaymentService를 생성합니다:
@Service
public class PaymentService {
private final Map<String, PaymentStrategy> paymentStrategies = new HashMap<>();
public PaymentService(List<PaymentStrategy> strategies) {
for (PaymentStrategy strategy : strategies) {
paymentStrategies.put(strategy.getClass().getSimpleName(), strategy);
}
}
public void processPayment(String strategyName, double amount) {
PaymentStrategy strategy = paymentStrategies.get(strategyName);
if (strategy != null) {
strategy.pay(amount);
} else {
throw new IllegalArgumentException("No such payment strategy: " + strategyName);
}
}
}
2. Enum 사용
Enums는 미리 정의된 상수 집합과 그와 관련된 동작을 나타내는 데 사용할 수 있습니다.
예제: 주문 상태 관리
다양한 동작을 가진 OrderStatus enum을 정의합니다:
public enum OrderStatus {
NEW {
@Override
public void handle() {
System.out.println("Processing new order.");
}
},
SHIPPED {
@Override
public void handle() {
System.out.println("Order shipped.");
}
},
DELIVERED {
@Override
public void handle() {
System.out.println("Order delivered.");
}
};
public abstract void handle();
}
이 enum을 서비스에서 사용합니다:
@Service
public class OrderService {
public void processOrder(OrderStatus status) {
status.handle();
}
}
3. 다형성 (Polymorphism)
다형성은 객체를 실제 클래스가 아닌 부모 클래스의 인스턴스로 취급할 수 있게 해줍니다. 이를 통해 부모 클래스의 참조를 통해 파생 클래스의 재정의된 메서드를 호출할 수 있습니다.
예제: 알림 시스템
Notification 인터페이스와 그 구현을 정의합니다:
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
@Override
public void send(String message) {
// 이메일 전송 로직
System.out.println("Sending email: " + message);
}
}
public class SmsNotification implements Notification {
@Override
public void send(String message) {
// SMS 전송 로직
System.out.println("Sending SMS: " + message);
}
}
다형성을 사용하는 서비스를 생성합니다:
@Service
public class NotificationService {
private final List<Notification> notifications;
public NotificationService(List<Notification> notifications) {
this.notifications = notifications;
}
public void notifyAll(String message) {
for (Notification notification : notifications) {
notification.send(message);
}
}
}
4. 람다 표현식과 함수형 인터페이스
람다 표현식은 특히 단일 메서드 인터페이스를 다룰 때 코드를 간소화할 수 있습니다.
예제: 할인 서비스
람다 표현식을 사용하는 할인 서비스를 정의합니다:
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class DiscountService {
private Map<String, Function<Double, Double>> discountStrategies = new HashMap<>();
public DiscountService() {
discountStrategies.put("SUMMER_SALE", price -> price * 0.9);
discountStrategies.put("WINTER_SALE", price -> price * 0.8);
}
public double applyDiscount(String discountCode, double price) {
return discountStrategies.getOrDefault(discountCode, Function.identity()).apply(price);
}
}
5. 명령 패턴 (Command Pattern)
명령 패턴은 요청을 객체로 캡슐화하여 클라이언트를 큐, 요청, 작업으로 매개변수화할 수 있게 합니다.
예제: 파일 작업
Command 인터페이스와 구체적인 명령을 정의합니다:
public interface Command {
void execute();
}
public class OpenFileCommand implements Command {
private FileSystemReceiver fileSystem;
public OpenFileCommand(FileSystemReceiver fs) {
this.fileSystem = fs;
}
@Override
public void execute() {
this.fileSystem.openFile();
}
}
public class CloseFileCommand implements Command {
private FileSystemReceiver fileSystem;
public CloseFileCommand(FileSystemReceiver fs) {
this.fileSystem = fs;
}
@Override
public void execute() {
this.fileSystem.closeFile();
}
}
FileSystemReceiver와 Invoker를 정의합니다:
public interface FileSystemReceiver {
void openFile();
void closeFile();
}
public class UnixFileSystemReceiver implements FileSystemReceiver {
@Override
public void openFile() {
System.out.println("Opening file in Unix OS");
}
@Override
public void closeFile() {
System.out.println("Closing file in Unix OS");
}
}
public class FileInvoker {
private Command command;
public FileInvoker(Command cmd) {
this.command = cmd;
}
public void execute() {
this.command.execute();
}
}
6. 가드 절 (Guard Clauses)
가드 절은 조건을 미리 처리하여 중첩 구조를 줄이고 코드를 더 읽기 쉽게 만듭니다.
예제: 사용자 검증
사용자 입력을 검증하기 위해 if-else 문을 중첩하는 대신, 가드 절을 사용하여 잘못된 경우를 미리 처리합니다:
public class UserService {
public void registerUser(User user) {
if (user == null) {
throw new IllegalArgumentException("User cannot be null");
}
if (user.getName() == null || user.getName().isEmpty()) {
throw new IllegalArgumentException("User name cannot be empty");
}
if (user.getEmail() == null || user.getEmail().isEmpty()) {
throw new IllegalArgumentException("User email cannot be empty");
}
// 등록 진행
System.out.println("Registering user: " + user.getName());
}
}
결론
이러한 전략을 적용함으로써 Java Spring Boot 프로젝트에서 if-else 문의 사용을 크게 줄일 수 있습니다. 이는 코드의 가독성을 높이고 유지보수성과 확장성을 향상시킵니다. 이러한 패턴과 관행을 채택하여 더 깨끗하고 효율적인 코드를 작성하세요.
출처
'레퍼런스' 카테고리의 다른 글
[Medium] JPA vs Hibernate vs Spring Data JPA (0) | 2024.08.21 |
---|---|
[Medium] 유용한 무료 IntelliJ Plugin 5가지 (0) | 2024.07.25 |
[Medium] Postgres vs MySQL (0) | 2024.06.26 |
[Medium] 엔지니어로서 사이드 비즈니스를 시작하는 5단계 (0) | 2024.06.12 |
[Medium] 모든 엔지니어가 필요한 15개의 시간 절약 사이트 (0) | 2024.06.06 |