인프런 워밍업클럽 4기 BE- 클린코드&테스트 DAY4 과제 (SOLID 원칙과 리팩토링)
4개월 전
<변환 전 코드>
public boolean validateOrder(Order order) {
if (order.getItems().size() == 0) {
log.info("주문 항목이 없습니다.");
return false;
} else {
if (order.getTotalPrice() > 0) {
if (!order.hasCustomerInfo()) {
log.info("사용자 정보가 없습니다.");
return false;
} else {
return true;
}
} else if (!(order.getTotalPrice() > 0)) {
log.info("올바르지 않은 총 가격입니다.");
return false;
}
}
return true;
}
<변환된 코드>
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
record Item(int price) {}
record Customer(String name) {}
class Order {
private final List<Item> items;
private final Customer customer;
Order(List<Item> items, Customer customer) {
this.items = items;
this.customer = customer;
}
void validate() {
if (items.isEmpty()) throw ex("주문 항목이 없습니다.");
if (totalPrice() <= 0) throw ex("올바르지 않은 총 가격입니다.");
if (customer == null) throw ex("사용자 정보가 없습니다.");
}
private int totalPrice() { return items.stream().mapToInt(Item::price).sum(); }
private IllegalStateException ex(String msg) { return new IllegalStateException(msg); }
}
class OrderValidator {
private static final Logger log = LoggerFactory.getLogger(OrderValidator.class);
boolean validateOrder(Order order) {
if (order == null) { log.info("주문 정보가 없습니다."); return false; }
try { order.validate(); return true; }
catch (IllegalStateException e) { log.info(e.getMessage()); return false; }
}
}
부정 연산자 제거
Early Return (예외를 던져 첫 실패시 즉시 반환함)
불필요한 클래스 제거 (OrderItems, ValidationResult 없애기)
레코드 사용 (Item, Customer를 Record로 사용해, 불변 데이터 정의를 간결화 함)
SOLID 원칙
단일 책임 원칙 (Single Responsibility Principle, SRP)
책임이 명확히 분리된 코드는 읽기 쉽고, 유지보수와 테스트가 쉬워진다.
여러 기능이 한 클래스에 섞이면 변경이 자주 발생하고, 코드의 복잡도가 증가한다.
개방-폐쇄 원칙 (Open/Closed Principle, OCP)
인터페이스나 추상 클래스를 활용해 유연하게 설계하면, 변화에 강한 구조를 만들 수 있다.
새로운 기능 추가 시 기존 코드를 수정하지 않고, 확장만으로 요구사항을 반영할 수 있어야 한다.
리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
상속 구조에서 자식 클래스가 부모 클래스의 규약을 깨지 않도록 설계해야 한다.
인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
하나의 큰 인터페이스보다는 여러 개의 구체적인 인터페이스로 분리하는 것이 좋다.
불필요한 의존성을 줄이고, 각 구현체가 꼭 필요한 기능만 구현하도록 유도할 수 있다.
의존 역전 원칙 (Dependency Inversion Principle, DIP)
구체적인 구현이 아니라, 인터페이스나 추상 클래스에 의존하도록 설계한다.
코드의 결합도가 낮아지고, 테스트와 확장이 쉬워진다.
댓글을 작성해보세요.