워밍업 클럽 4기 - 백엔드 Day 4 [객체 지향 패러다임]

1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.

As-Is

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

To-Be

public class Item {
    private String name;
    private int quantity;
    private int unitPrice;

    public Item(String name, int quantity, int unitPrice) {
        this.name = name;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
    }

    public int getQuantity() {
        return quantity;
    }

    public int getSubtotal() {
        return quantity * unitPrice;
    }
}
public class Order {
    private List<Item> items;
    private Customer customer;

    public Order(List<Item> items, Customer customer) {
        this.items = items;
        this.customer = customer;
    }

    public int getTotalItemCount() {
        return items.stream()
            .mapToInt(Item::getQuantity)
            .sum();
    }

    public int getTotalPrice() {
        return items.stream()
            .mapToInt(Item::getSubtotal)
            .sum();
    }

    public boolean isCustomerInfoEmpty() {
        return customer == null || customer.isEmpty();
    }
}
public class Customer {
    private String name;
    private String email;

    public Customer(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public boolean isEmpty() {
        return (name == null || name.isBlank()) &&
            (email == null || email.isBlank());
    }
}
public class OrderValidator {
    public void isValid(Order order) {
        if (isEmpty(order)) {
            System.out.println("주문 항목이 없습니다.");
            return;
        }

        if (isPriceInvalid(order)) {
            System.out.println("올바르지 않은 총 가격입니다.");
            return;
        }

        if (isMissingCustomerInfo(order)) {
            System.out.println("사용자 정보가 없습니다.");
            return;
        }

        System.out.println("유효성 검사를 통과했습니다.");
    }

    private boolean isEmpty(Order order) {
        return order.getTotalItemCount() == 0;
    }

    private boolean isPriceInvalid(Order order) {
        return order.getTotalPrice() <= 0;
    }

    private boolean isMissingCustomerInfo(Order order) {
        return order.isCustomerInfoEmpty();
    }
}

테스트

public class AppRunner {
    public static void main(String[] args) {
        OrderValidator validator = new OrderValidator();

        System.out.println("======= 주문 유효성 테스트 시작 =======");

        Order emptyOrder = new Order(Collections.emptyList(), null);
        validator.isValid(emptyOrder);

        List<Item> items1 = List.of(
            new Item("노트북", 1, 1000000)
        );
        Order noCustomerOrder = new Order(items1, null);
        validator.isValid(noCustomerOrder);

        List<Item> items2 = List.of(
            new Item("샤프", 1, -100)
        );
        Customer customer1 = new Customer("홍길동", "hong@example.com");
        Order negativePriceOrder = new Order(items2, customer1);
        validator.isValid(negativePriceOrder);

        List<Item> items3 = List.of(
            new Item("모니터", 2, 150000)
        );
        Customer customer2 = new Customer("김철수", "kim@example.com");
        Order validOrder = new Order(items3, customer2);
        validator.isValid(validOrder);

        System.out.println("======= 테스트 종료 =======");
    }
}

image


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

SRP

하나의 트레이에 하나의 물건을 담아야 정리하기 편하다.
-> 클래스도 하나의 책임만 가져야 유지보수 편리

OCP

멀티탭에 새 기기 꽂는 것은 쉬운 데에 반해, 멀티탭 자체를 고치는 것은 힘들다.
-> 기존 코드를 고치지 않고 기능을 추가할 수 있도록 설계

LSP

차를 렌트할 때, 차의 브랜드와 상관 없이 운전자는 차를 똑같이 운전할 수 있어야 한다.
-> 부모(인터페이스)를 기준으로 만들어진 기능은, 자식 바꿔 끼워도 문제 없이 동작

ISP

스마트폰 사용자마다 필요한 어플리케이션만 다운 받아서 사용한다.
-> 사용자가 쓰는 기능만 제공받도록 설계

DIP

이어폰을 바꾸어도 음악이 잘 들려야 한다.
-> 고정된 구현이 나이라 추상적 연결(인터페이스)에 의존


(Readable Code: 읽기 좋은 코드를 작성하는 사고법)

댓글을 작성해보세요.

채널톡 아이콘