나의 개발일지

[인프런 워밍업 클럽 3기](Day 4 미션) 리팩토링하기, SOLID 설명하기 본문

스터디/인프런 워밍업 클럽 백엔드 코드 3기

[인프런 워밍업 클럽 3기](Day 4 미션) 리팩토링하기, SOLID 설명하기

사각분무기 2025. 3. 6. 15:48

미션 설명

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;
}

2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.

나의 답

첫번째 문항

public static final String NO_ORDER_ITEM = "주문 항목이 없습니다.";
public static final String INVALID_TOTAL_PRICE = "올바르지 않은 총 가격입니다.";
public static final String NO_USER_INFO = "사용자 정보가 없습니다.";

public boolean validateOrder(Order order) throws OrderException {

    if (order.doesHaveItem()) {
        throw new OrderException(NO_ORDER_ITEM);
    }

    if (order.doesNotHaveValidTotalPrice()) {
        throw new OrderException(INVALID_TOTAL_PRICE);
    }

    if (order.doesNotHaveCustomerInfo()) {
        throw new OrderException(NO_USER_INFO);
    }

    return true;
}

Order

public abstract class Order {

    public abstract boolean doesHaveItem();

    public abstract boolean doesNotHaveValidTotalPrice();

    public abstract boolean doesNotHaveCustomerInfo();
}

변경 사항

  1. if문의 조건들을 하나의 함수로 정의함으로써 조건의 의미를 명확히 했습니다.
  2. if문의 조건에 부합하지 않을 경우 바로 결과를 반환하게 했습니다.
  3. 불필요한 부정 조건을 제거했습니다.
  4. 별개의 예외 처리 클래스를 생성하여 예외를 명확히 했습니다.
  5. 관심사를 기준으로 공백을 두었습니다.

두번째 문항

  • S
    • SRP (단일 책임 원칙)
    • 하나의 클래스는 하나의 책임(=관심사)을 가져야 한다.
    • SRP를 지킴으로써 객체들을 관심사 기준으로 분리할 수 있다.
    • 높은 응집도와 낮은 결합도를 제공한다.
      • 응집도
        • 클래스나 모듈 내 요소들이 긴밀하게 연관되어있는 정도
      • 결합도
        • 한 요소가 변경되었을 때 다른 요소들이 영향을 받는 정도
  • O
    • OCP (개방-폐쇄 원칙)
    • 확장에는 열려 있고, 수정에는 닫혀 있어야 한다.
    • 기존 코드의 변경 없이도 시스템의 기능을 확장할 수 있어야 한다.
    • 추상화와 다형성을 활용함으로써 구현할 수 있다.
  • L
    • LSP (리스코프 치환 원칙)
    • 두 클래스가 상속 구조를 가질 때 부모 클래스의 인스턴스를 자식 클래스로 치환하여도 기능 상에 문제가 없어야 한다.
    • 자식 클래스는
      • 부모 클래스의 책임을 준수해야 한다.
      • 부모 클래스의 행동을 변경하지 않아야 한다.
    • LSP를 위반할 경우 아래 문제가 발생할 수 있다.
      • 오동작
      • 예상 밖의 예외
      • 위 두 문제를 방지하기 위한 불필요한 타입 체크 동반
  • I
    • ISP (인터페이스 분리 원칙)
    • 클라이언트는 자신이 사용하지 않은 인터페이스에 의존하면 안된다.
    • ISP를 위반할 경우
      • 불필요한 의존성으로 인해 결합도가 높아진다.
      • 특정 기능의 변경이 여러 클래스에 영향을 미칠 수 있다.
    • 필요한 기능 단위로 인터페이스를 나눠서 사용해라.
  • D
    • DIP (의존성 역전 원칙)
    • 상위 수준의 모듈은 하위 수준의 모듈에 직접 의존해서는 안된다.
    • 추상화(인터페이스, 추상 클래스)에 의존해야 한다.
    • 의존성의 순방향
      • 고수준 모듈이 저수준 모듈을 직접 참조
    • 의존성의 역방향
      • 고수준, 저수준 모듈 모두 추상화를 참조
    • DIP를 지킬 경우 저수준 모듈의 변경이 고수준 모듈에 영향을 미치지 않게 된다.