ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,86 @@
+package store.controller;
+
+import static store.util.Constant.ANSWER_NO;
+import static store.util.InformationMessage.WELCOME_MESSAGE;
+import static store.util.Validator.validateAnswer;
+import static store.view.InputView.readForAdditionalPurchase;
+import static store.view.OutputView.displayReceipt;
+
+import camp.nextstep.edu.missionutils.Console;
+import java.io.IOException;
+import store.service.PaymentService;
+import store.domain.Inventory;
+import store.domain.Order;
+import store.domain.Promotions;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class StoreController {
+
+    private final Inventory inventory;
+    private final Promotions promotions;
+
+    public StoreController() {
+        inventory = prepareInventory();
+        promotions = preparePromotions();
+    }
+
+    public void run() {
+        do {
+            OutputView.printMessage(WELCOME_MESSAGE);
+            OutputView.printInventory(inventory);
+            Order order = inputOrder(inventory);
+            PaymentService paymentService = new PaymentService(inventory, promotions, order);
+            displayReceipt(paymentService.getReceipt());
+        } while (!inputAdditionalPurchase().equals(ANSWER_NO));
+        closeConsole();
+    }
+
+    Inventory prepareInventory() {
+        Inventory inventory;
+        try {
+            inventory = new Inventory();
+            return inventory;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    Promotions preparePromotions() {
+        Promotions promotions;
+        try {
+            promotions = new Promotions();
+            return promotions;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    Order inputOrder(Inventory inventory) {
+        while (true) {
+            try {
+                Order order = new Order(InputView.readOrder());
+                inventory.checkOrder(order);
+                return order;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e.getMessage());
+            }
+        }
+    }
+
+    String inputAdditionalPurchase() {
+        while (true) {
+            try {
+                String answer = readForAdditionalPurchase();
+                validateAnswer(answer);
+                return answer;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e.getMessage());
+            }
+        }
+    }
+
+    void closeConsole() {
+        Console.close();
+    }
+}

리뷰: private로 안하신 이유가 있나요? controller에서는 아무나 접근하지 못하도록 접근제한자를 설정해 놓는게 좋아보입니다
@@ -0,0 +1,83 @@
+package store.domain;
+
+import static store.util.Constant.BLANK;
+import static store.util.ErrorMessage.INVALID_ORDER_MESSAGE;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Order {
+
+    private final Map<String, Integer> orders;
+
+    public Order(String orderInput) {
+        this.orders = new LinkedHashMap<>();
+        validate(orderInput);
+        putToOrders(orderInput);
+    }
+
+    private void putToOrders(String orderInput) {
+        List<String> orderParts = List.of(orderInput.split(","));
+        for (String orderPart : orderParts) {
+            parseOrder(orderPart);
+        }
+    }
+
+    private void parseOrder(String orderPart) {
+        String orderPartNoBracket = removeSquareBracket(orderPart);
+        List<String> productNameAndQuantity = List.of(orderPartNoBracket.split("-"));
+        String name = productNameAndQuantity.getFirst();
+        String quantityPart = productNameAndQuantity.getLast();
+        int quantity = Integer.parseInt(quantityPart);
+        orders.put(name, quantity);
+    }
+
+    private void validate(String orderInput) {
+        List<String> splitComma = List.of(orderInput.split(","));
+        for (String productNameAndQuantity : splitComma) {
+            checkSquareBracket(productNameAndQuantity);
+            checkSeparatedTwoPart(productNameAndQuantity);
+        }
+    }
+
+    private void checkSquareBracket(String order) {
+        if (!isStartAndEndWithSquareBracket(order)) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private void checkSeparatedTwoPart(String order) {
+        String orderNoBracket = removeSquareBracket(order);
+        List<String> splitHyphen = List.of(orderNoBracket.split("-"));
+        if (splitHyphen.size() != 2) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+        String quantity = splitHyphen.getLast();
+        checkQuantityPart(quantity);
+    }
+
+    private void checkQuantityPart(String quantityPart) {
+        try {
+            int quantity = Integer.parseInt(quantityPart);
+            if (quantity <= 0) {
+                throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+            }
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private boolean isStartAndEndWithSquareBracket(String order) {
+        return order.startsWith("[") && order.endsWith("]");
+    }
+
+    private String removeSquareBracket(String orderPart) {
+        return orderPart.replaceAll("[\\[\\]]", BLANK);
+    }
+
+    public Map<String, Integer> getOrders() {
+        return Collections.unmodifiableMap(orders);
+    }
+}

리뷰: 저도 도메인에서 검증하는 게 있는걸 좋아하는 편인데 해당 제가 생각하는 이번 편의점에서는 orderCount를 미리 parse하는 게 더 좋다고 생각합니다
@@ -0,0 +1,86 @@
+package store.controller;
+
+import static store.util.Constant.ANSWER_NO;
+import static store.util.InformationMessage.WELCOME_MESSAGE;
+import static store.util.Validator.validateAnswer;
+import static store.view.InputView.readForAdditionalPurchase;
+import static store.view.OutputView.displayReceipt;
+
+import camp.nextstep.edu.missionutils.Console;
+import java.io.IOException;
+import store.service.PaymentService;
+import store.domain.Inventory;
+import store.domain.Order;
+import store.domain.Promotions;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class StoreController {
+
+    private final Inventory inventory;
+    private final Promotions promotions;
+
+    public StoreController() {
+        inventory = prepareInventory();
+        promotions = preparePromotions();
+    }
+
+    public void run() {
+        do {
+            OutputView.printMessage(WELCOME_MESSAGE);
+            OutputView.printInventory(inventory);
+            Order order = inputOrder(inventory);
+            PaymentService paymentService = new PaymentService(inventory, promotions, order);
+            displayReceipt(paymentService.getReceipt());
+        } while (!inputAdditionalPurchase().equals(ANSWER_NO));
+        closeConsole();
+    }
+
+    Inventory prepareInventory() {
+        Inventory inventory;
+        try {
+            inventory = new Inventory();
+            return inventory;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    Promotions preparePromotions() {
+        Promotions promotions;
+        try {
+            promotions = new Promotions();
+            return promotions;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    Order inputOrder(Inventory inventory) {
+        while (true) {
+            try {
+                Order order = new Order(InputView.readOrder());
+                inventory.checkOrder(order);
+                return order;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e.getMessage());
+            }
+        }
+    }
+
+    String inputAdditionalPurchase() {
+        while (true) {
+            try {
+                String answer = readForAdditionalPurchase();
+                validateAnswer(answer);
+                return answer;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e.getMessage());
+            }
+        }
+    }
+
+    void closeConsole() {
+        Console.close();
+    }
+}

리뷰: 해당 코드는 InputView에서 메서드를 만들어서 호출하는 방식으로 수행했으면 더 좋았을 것 같다는 생각이 들었어요!
import camp.nextstep.edu.missionutils.Console; 동일한 import 구문이 InputView에도 있기 때문이에요!
@@ -0,0 +1,83 @@
+package store.domain;
+
+import static store.util.Constant.BLANK;
+import static store.util.ErrorMessage.INVALID_ORDER_MESSAGE;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Order {
+
+    private final Map<String, Integer> orders;
+
+    public Order(String orderInput) {
+        this.orders = new LinkedHashMap<>();
+        validate(orderInput);
+        putToOrders(orderInput);
+    }
+
+    private void putToOrders(String orderInput) {
+        List<String> orderParts = List.of(orderInput.split(","));
+        for (String orderPart : orderParts) {
+            parseOrder(orderPart);
+        }
+    }
+
+    private void parseOrder(String orderPart) {
+        String orderPartNoBracket = removeSquareBracket(orderPart);
+        List<String> productNameAndQuantity = List.of(orderPartNoBracket.split("-"));
+        String name = productNameAndQuantity.getFirst();
+        String quantityPart = productNameAndQuantity.getLast();
+        int quantity = Integer.parseInt(quantityPart);
+        orders.put(name, quantity);
+    }
+
+    private void validate(String orderInput) {
+        List<String> splitComma = List.of(orderInput.split(","));
+        for (String productNameAndQuantity : splitComma) {
+            checkSquareBracket(productNameAndQuantity);
+            checkSeparatedTwoPart(productNameAndQuantity);
+        }
+    }
+
+    private void checkSquareBracket(String order) {
+        if (!isStartAndEndWithSquareBracket(order)) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private void checkSeparatedTwoPart(String order) {
+        String orderNoBracket = removeSquareBracket(order);
+        List<String> splitHyphen = List.of(orderNoBracket.split("-"));
+        if (splitHyphen.size() != 2) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+        String quantity = splitHyphen.getLast();
+        checkQuantityPart(quantity);
+    }
+
+    private void checkQuantityPart(String quantityPart) {
+        try {
+            int quantity = Integer.parseInt(quantityPart);
+            if (quantity <= 0) {
+                throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+            }
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private boolean isStartAndEndWithSquareBracket(String order) {
+        return order.startsWith("[") && order.endsWith("]");
+    }
+
+    private String removeSquareBracket(String orderPart) {
+        return orderPart.replaceAll("[\\[\\]]", BLANK);
+    }
+
+    public Map<String, Integer> getOrders() {
+        return Collections.unmodifiableMap(orders);
+    }
+}

리뷰: 쉼표(,)와 같은 의미 있는 기호를 상수로써 분리를 하시면 유지 보수 관점에서 더 좋을 것 같다는 생각이 들었어요!
@@ -0,0 +1,83 @@
+package store.domain;
+
+import static store.util.Constant.BLANK;
+import static store.util.ErrorMessage.INVALID_ORDER_MESSAGE;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Order {
+
+    private final Map<String, Integer> orders;
+
+    public Order(String orderInput) {
+        this.orders = new LinkedHashMap<>();
+        validate(orderInput);
+        putToOrders(orderInput);
+    }
+
+    private void putToOrders(String orderInput) {
+        List<String> orderParts = List.of(orderInput.split(","));
+        for (String orderPart : orderParts) {
+            parseOrder(orderPart);
+        }
+    }
+
+    private void parseOrder(String orderPart) {
+        String orderPartNoBracket = removeSquareBracket(orderPart);
+        List<String> productNameAndQuantity = List.of(orderPartNoBracket.split("-"));
+        String name = productNameAndQuantity.getFirst();
+        String quantityPart = productNameAndQuantity.getLast();
+        int quantity = Integer.parseInt(quantityPart);
+        orders.put(name, quantity);
+    }
+
+    private void validate(String orderInput) {
+        List<String> splitComma = List.of(orderInput.split(","));
+        for (String productNameAndQuantity : splitComma) {
+            checkSquareBracket(productNameAndQuantity);
+            checkSeparatedTwoPart(productNameAndQuantity);
+        }
+    }
+
+    private void checkSquareBracket(String order) {
+        if (!isStartAndEndWithSquareBracket(order)) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private void checkSeparatedTwoPart(String order) {
+        String orderNoBracket = removeSquareBracket(order);
+        List<String> splitHyphen = List.of(orderNoBracket.split("-"));
+        if (splitHyphen.size() != 2) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+        String quantity = splitHyphen.getLast();
+        checkQuantityPart(quantity);
+    }
+
+    private void checkQuantityPart(String quantityPart) {
+        try {
+            int quantity = Integer.parseInt(quantityPart);
+            if (quantity <= 0) {
+                throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+            }
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private boolean isStartAndEndWithSquareBracket(String order) {
+        return order.startsWith("[") && order.endsWith("]");
+    }
+
+    private String removeSquareBracket(String orderPart) {
+        return orderPart.replaceAll("[\\[\\]]", BLANK);
+    }
+
+    public Map<String, Integer> getOrders() {
+        return Collections.unmodifiableMap(orders);
+    }
+}

리뷰: split을 사용하셨지만 List를 이용하시기 위헤 List.of를 이용하신 점이 돋보이네요!
@@ -0,0 +1,86 @@
+package store.controller;
+
+import static store.util.Constant.ANSWER_NO;
+import static store.util.InformationMessage.WELCOME_MESSAGE;
+import static store.util.Validator.validateAnswer;
+import static store.view.InputView.readForAdditionalPurchase;
+import static store.view.OutputView.displayReceipt;
+
+import camp.nextstep.edu.missionutils.Console;
+import java.io.IOException;
+import store.service.PaymentService;
+import store.domain.Inventory;
+import store.domain.Order;
+import store.domain.Promotions;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class StoreController {
+
+    private final Inventory inventory;
+    private final Promotions promotions;
+
+    public StoreController() {
+        inventory = prepareInventory();
+        promotions = preparePromotions();
+    }
+
+    public void run() {
+        do {
+            OutputView.printMessage(WELCOME_MESSAGE);
+            OutputView.printInventory(inventory);
+            Order order = inputOrder(inventory);
+            PaymentService paymentService = new PaymentService(inventory, promotions, order);
+            displayReceipt(paymentService.getReceipt());
+        } while (!inputAdditionalPurchase().equals(ANSWER_NO));
+        closeConsole();
+    }
+
+    Inventory prepareInventory() {
+        Inventory inventory;
+        try {
+            inventory = new Inventory();
+            return inventory;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    Promotions preparePromotions() {
+        Promotions promotions;
+        try {
+            promotions = new Promotions();
+            return promotions;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    Order inputOrder(Inventory inventory) {
+        while (true) {
+            try {
+                Order order = new Order(InputView.readOrder());
+                inventory.checkOrder(order);
+                return order;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e.getMessage());
+            }
+        }
+    }
+
+    String inputAdditionalPurchase() {
+        while (true) {
+            try {
+                String answer = readForAdditionalPurchase();
+                validateAnswer(answer);
+                return answer;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e.getMessage());
+            }
+        }
+    }
+
+    void closeConsole() {
+        Console.close();
+    }
+}

리뷰: 일반적으로 do-while보다는 while문을 사용하여 조건을 먼저 확인하는 것이 더 명확하고 가독성도 향상된다고 알고 있어요! while 문을 사용한다면, 코드 흐름이 명확해져서 유지보수할 때 더 쉽게 이해할 수 있을 것 같습니다. 😊
@@ -0,0 +1,83 @@
+package store.domain;
+
+import static store.util.Constant.BLANK;
+import static store.util.ErrorMessage.INVALID_ORDER_MESSAGE;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Order {
+
+    private final Map<String, Integer> orders;
+
+    public Order(String orderInput) {
+        this.orders = new LinkedHashMap<>();
+        validate(orderInput);
+        putToOrders(orderInput);
+    }
+
+    private void putToOrders(String orderInput) {
+        List<String> orderParts = List.of(orderInput.split(","));
+        for (String orderPart : orderParts) {
+            parseOrder(orderPart);
+        }
+    }
+
+    private void parseOrder(String orderPart) {
+        String orderPartNoBracket = removeSquareBracket(orderPart);
+        List<String> productNameAndQuantity = List.of(orderPartNoBracket.split("-"));
+        String name = productNameAndQuantity.getFirst();
+        String quantityPart = productNameAndQuantity.getLast();
+        int quantity = Integer.parseInt(quantityPart);
+        orders.put(name, quantity);
+    }
+
+    private void validate(String orderInput) {
+        List<String> splitComma = List.of(orderInput.split(","));
+        for (String productNameAndQuantity : splitComma) {
+            checkSquareBracket(productNameAndQuantity);
+            checkSeparatedTwoPart(productNameAndQuantity);
+        }
+    }
+
+    private void checkSquareBracket(String order) {
+        if (!isStartAndEndWithSquareBracket(order)) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private void checkSeparatedTwoPart(String order) {
+        String orderNoBracket = removeSquareBracket(order);
+        List<String> splitHyphen = List.of(orderNoBracket.split("-"));
+        if (splitHyphen.size() != 2) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+        String quantity = splitHyphen.getLast();
+        checkQuantityPart(quantity);
+    }
+
+    private void checkQuantityPart(String quantityPart) {
+        try {
+            int quantity = Integer.parseInt(quantityPart);
+            if (quantity <= 0) {
+                throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+            }
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private boolean isStartAndEndWithSquareBracket(String order) {
+        return order.startsWith("[") && order.endsWith("]");
+    }
+
+    private String removeSquareBracket(String orderPart) {
+        return orderPart.replaceAll("[\\[\\]]", BLANK);
+    }
+
+    public Map<String, Integer> getOrders() {
+        return Collections.unmodifiableMap(orders);
+    }
+}

리뷰: 음,, 혹시 이러면 [콜라[-3]] 이렇게 작성돼도 통과될 거 같은데 아닌가요 ??
@@ -0,0 +1,40 @@
+package store.domain;
+
+import java.time.LocalDateTime;
+
+public class Promotion {
+
+    private final String name;
+    private final int buy;
+    private final int get;
+    private final LocalDateTime startDate;
+    private final LocalDateTime endDate;
+
+    public Promotion(String name, int buy, int get, LocalDateTime startDate, LocalDateTime endDate) {
+        this.name = name;
+        this.buy = buy;
+        this.get = get;
+        this.startDate = startDate;
+        this.endDate = endDate;
+    }
+
+    public boolean canApply(LocalDateTime now) {
+        return now.isAfter(startDate) && now.isBefore(endDate);
+    }
+
+    public int calculateBundle() {
+        return buy + get;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getBuy() {
+        return buy;
+    }
+
+    public int getGet() {
+        return get;
+    }
+}

리뷰: startDate와 endDate 는 DateRange 같은 클래스를 통해 따로 분리하는 것이 나을 것 같아요!
@@ -0,0 +1,40 @@
+package store.domain;
+
+import java.time.LocalDateTime;
+
+public class Promotion {
+
+    private final String name;
+    private final int buy;
+    private final int get;
+    private final LocalDateTime startDate;
+    private final LocalDateTime endDate;
+
+    public Promotion(String name, int buy, int get, LocalDateTime startDate, LocalDateTime endDate) {
+        this.name = name;
+        this.buy = buy;
+        this.get = get;
+        this.startDate = startDate;
+        this.endDate = endDate;
+    }
+
+    public boolean canApply(LocalDateTime now) {
+        return now.isAfter(startDate) && now.isBefore(endDate);
+    }
+
+    public int calculateBundle() {
+        return buy + get;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getBuy() {
+        return buy;
+    }
+
+    public int getGet() {
+        return get;
+    }
+}

리뷰: 저도 실수한 부분인데, 이러면 프로모션 시작일, 종료일이면 false 를 반환하더라구요 ㅜㅜ
@@ -1,7 +1,20 @@
 package christmas;
 
+import christmas.domain.EventController;
+import christmas.global.config.Dependency;
+import christmas.global.view.output.OutputView;
+
+import static christmas.global.exception.CommonExceptionMessage.EXCEPTION_PREFIX;
+
 public class Application {
     public static void main(String[] args) {
         // TODO: 프로그램 구현
+        try {
+            EventController eventController = Dependency.eventController();
+            eventController.play();
+        } catch (Exception e) {
+            OutputView.println(EXCEPTION_PREFIX.get() + e.getMessage());
+            throw e;
+        }
     }
 }

리뷰: 잘못된 입력을 모두 예외 클래스로 만들어서 Application 내에서 catch되는게 흥미롭고 효율적인 것 같네요! 잘 배우고 갑니다
@@ -0,0 +1,32 @@
+package christmas.domain.sale.enums;
+
+import christmas.domain.calendar.enums.Week;
+import christmas.domain.order.enums.food.FoodCategory;
+
+import java.util.Arrays;
+
+import static christmas.domain.calendar.enums.Week.WEEKDAY;
+import static christmas.domain.calendar.enums.Week.WEEKEND;
+import static christmas.domain.order.enums.food.FoodCategory.DESSERT;
+import static christmas.domain.order.enums.food.FoodCategory.MAIN_DISH;
+
+public enum SaleTarget {
+    MAIN_FOR_SALE(WEEKEND, MAIN_DISH),
+    DESSERT_FOR_SALE(WEEKDAY, DESSERT);
+
+    private final Week week;
+    private final FoodCategory foodCategory;
+
+    SaleTarget(Week week, FoodCategory foodCategory) {
+        this.week = week;
+        this.foodCategory = foodCategory;
+    }
+
+    public static FoodCategory findByDay(Week week) {
+        return Arrays.stream(SaleTarget.values())
+                .filter(target -> target.week.equals(week))
+                .findFirst()
+                .map(saleTarget -> saleTarget.foodCategory)
+                .get();
+    }
+}

리뷰: SaleTarget이라는 enum으로 평일과 주말의 할인을 처리하신 부분이 굉장히 흥미로운데 일반 클래스가 아니라 enum으로 하신 이유가 무엇인가요?
@@ -0,0 +1,25 @@
+package christmas.domain.calendar.dto;
+
+import static christmas.global.exception.DateExceptionMessage.INVALID_DATE;
+
+public record DateDto(String date) {
+
+    public DateDto {
+        validateDateIsNotNull(date);
+        validateDateIsNumber(date);
+    }
+
+    private void validateDateIsNotNull(String date) {
+        if (date == null) {
+            throw new IllegalArgumentException(INVALID_DATE.get());
+        }
+    }
+
+    private void validateDateIsNumber(String date) {
+        try {
+            Integer.parseInt(date);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(INVALID_DATE.get());
+        }
+    }
+}

리뷰: 코드 읽다가 record에 대해 새롭게 알게 되었습니다. 정말 감사합니다 🥹