![[워밍업 클럽 4기 백엔드] Day4 미션](https://cdn.inflearn.com/public/files/blogs/56286df2-1d1b-428b-bdaa-5477bcf4bd8a/워밍업 클럽 이미지.png)
[워밍업 클럽 4기 백엔드] Day4 미션
미션
1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.
[리팩토링 전]
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;
}
순서
early return 적용
Order 도메인 생성 및 구현
OrderService, Item, CustomerInfo 생성 및 구현
조건문의 조건을 메서드 추출해 추상화 레벨 맞추기
OrderService의 위 메서드 명이 validateOrder()일 경우 boolean 반환을 void 반환으로 변경
로그 출력을 예외 처리로 변경
if 분기문을 하나씩 메서드 추출
try-catch로 감싸기 or AOP를 이용한 Exception 처리
후보
OrderService의 위 메서드가 boolean을 반환할 경우 메서드 명을 isValidOrder()로 변경
로그 출력 형태 유지
+
is와 has
is는 상태를 확인하기 위해 사용했고 (empty, enabled, active 등)
has는 특정 정보나 구성 요소가 있는지 확인하기 위해 사용했습니다.
[리팩토링 후]
OrderService
public void validateOrder(Order order) {
try {
validateItemsExist(order);
validateTotalPrice(order);
validateCustomerInfo(order);
} catch (InvalidOrderException e) {
log.error("검증 실패: {}", e.getMessage(), e);
} catch (Exception e) {
log.error("서비스에 문제가 발생했습니다.", e);
}
}
private static void validateCustomerInfo(Order order) {
if (order.hasNoCustomerInfo()) {
throw new InvalidOrderException("사용자 정보가 없습니다.");
}
}
private static void validateTotalPrice(Order order) {
if (order.isInvalidTotalPrice()) {
throw new InvalidOrderException("올바르지 않은 총 가격입니다.");
}
}
private static void validateItemsExist(Order order) {
if (order.hasNoItems()) {
throw new InvalidOrderException("주문 항목이 없습니다.");
}
}
Order
public class Order {
private List<Item> items;
private CustomerInfo customerInfo;
private int totalPrice;
public boolean hasNoCustomerInfo() {
return customerInfo == null;
}
public boolean hasNoItems() {
return getItems().isEmpty();
}
public boolean isInvalidTotalPrice() {
return calculateTotalPrice() <= 0;
}
public int calculateTotalPrice() { // 계산 로직이 있다고 가정함
return totalPrice;
}
public List<Item> getItems() {
return items;
}
}
Item, CustomerInfo (클래스만 생성했습니다)
public class Item {
}
public class CustomerInfo {
}
InvalidOrderException
public class InvalidOrderException extends RuntimeException {
public InvalidOrderException(String message) {
super(message);
}
}
2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.
SOLID를 직관적으로 이해하고 싶어
영단어의 어원과 이미지를 찾아보며 정리해봤습니다
SRP (Single Responsibility Principle)
"A class should have only one reason to change"
단일 책임의 원칙
spondere: 그렇게 하겠소라고 약속하는, 대답하는 이미지
re-: 순환해서 다시 돌아오는 이미지
respondere: 상대방이 물어보거나 요구해서 (변화나 반응을 일으킨다고 이해함) 약속하겠다라고 대답을 돌려주는 이미지
able: 어떤 일을 할 자격과 조건이 갖추어진 만반의 상태
responsibility: 상대방이 나에게 무언가를 요구하거나 질문했을 때, (변화 요구라고 이해했습니다)
이를 들어주겠다 대답할 능력과 준비가 되어있는 만반의 상태
정리
Single Responsibility Principle은
오직 한 종류의 변화(요구)에 대해 들어주겠다며 대답할 능력과 준비가 돼있어야한다는 것으로 이해했습니다.
OCP (Open-closed Principle)
"Software entities should be open for extension, but closed for modification"
개방-폐쇄 원칙
extension: 밖에 덧붙이는 이미지
modification: 안쪽을 다 뜯어고치는 이미지
정리
OCP는 "변화가 왔을때 어떻게 대응할 것인가"에 대한 규칙이라고 생각됩니다.
변화가 왔을때 기존의 것을 뜯어고치는게 아닌 덧붙이는 방식이라고 이해했습니다.
LSP (Liskov Substitution Principle)
"Subtypes must be substitutable for their base types"
리스코프-치환 법칙
sub-: under, below, 원래의 것에 속해있거나 보조하는 식으로 위계질서의 하단부 or 물리적으로 아래에 있는 이미지
stituere: to place (무엇인가를 세워놓은 느낌)
substitute: 원래 있던 자리에 다른 것을 대신해서 세워놓는 이미지
정리
서브 타입을 원래의 것 대신 세울 수 있어야한다. 자식이 부모를 대신할 수 있어야 한다.
위 둘을 집합의 개념으로 이해했습니다.
ISP (Interface Segregation Principle)
"Clients should not be forced to depend on interfaces they do not use"
인터페이스 분리 법칙
se-: 떨어지는 느낌
gregare: 무리짓다, 그룹짓다
segregate: 무리(같은 타입)에서 떨어트려, 새로운 무리로 구분하는 이미지
정리
사용하지 않는 인터페이스 기능이 있다면 쪼개서, 사용하는 기능과 사용하지 않는 기능으로 인터페이스로 분리하고 그것에 의존하는 것이라고 이해했습니다.
DIP (Dependency Inversion Principle)
"High-level modules should not depend on low-level modules. Both should depend on abstractions"
"Abstractions should not depend on details. Details should depend on abstractions"
의존성 역전 원칙
de-: down, away (위에서 아래로 떨어지는 이미지, 방향을 가짐)
pendere: to hang (매달려있는 이미지)
depend: 아래로 축 쳐진 상태에서 매달려 있는 이미지
in-: 안쪽에 있는 기준점으로 향하는 이미지
vertere: to turn 축(선)을 기준으로 180도 회전하는 이미지
inverse: 밖으로 가고있던 방향을 180도 뒤집어서, 안쪽에 있는 기준점으로 향하는 느낌
정리
depend와 inverse를 조합하면
위에서 아래로 매달리던 것을, 아래에서 위로 매달리도록 방향을 180도 뒤집는 이미지입니다.
그러나 실제로는 중간에 인터페이스를 둬서 위에선 중간에 의존하고, 아래에서도 중간에 의존하는 이미지라
방향 전환이라고만 생각한다면 한 번 생각을 거치게 되는 것 같습니다.
따라서 의존성 추상화 원칙(Dependency Abstraction Principle)이라고 생각하는 것도 직관적이고 이해에 좋을 것 같습니다.
강의
댓글을 작성해보세요.