ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,20 @@
+package message;
+
+public enum IOMessage {
+    INVENTORY_STATUS_MESSAGE("안녕하세요. W편의점입니다.\n현재 보유하고 있는 상품입니다.\n"),
+    ORDER_MESSAGE("구매하실 상품명과 수량을 입력해 주세요. (예: [사이다-2],[감자칩-1])"),
+    PARTIAL_PROMOTION_MESSAGE("현재 %s %d개는 프로모션 할인이 적용되지 않습니다. 그래도 구매하시겠습니까? (Y/N)"),
+    ADDITIONAL_PROMOTION_QUANTITY_MESSAGE("현재 %s은(는) 1개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)"),
+    MEMBERSHIP_DISCOUNT_MESSAGE("멤버십 할인을 받으시겠습니까? (Y/N)"),
+    EXIT_CHECK_MESSAGE("감사합니다. 구매하고 싶은 다른 상품이 있나요? (Y/N)");
+
+    private final String ioMessage;
+
+    IOMessage(final String ioMessage) {
+        this.ioMessage = ioMessage;
+    }
+
+    public String getMessage() {
+        return ioMessage;
+    }
+}
\ No newline at end of file

리뷰: 기능에 따라 enum을 구분하셨군요 ! 추후에 관리하기 좋을 것 같네요 배워갑니다 👍
@@ -0,0 +1,32 @@
+package model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class Receipts {
+    private static final List<Purchase> receipts = new ArrayList<>();
+
+    private Receipts() {}
+
+    public static void clear() {
+        receipts.clear();
+    }
+
+    public static void add(Purchase purchase) {
+        receipts.add(purchase);
+    }
+
+    public static int membershipDiscountEligiblePrice() {
+        int price = 0;
+        for (Purchase purchase : receipts) {
+            int discountEligiblePrice = (purchase.price() * purchase.quantity())
+                    - (purchase.promotionQuantity() * purchase.buyPlusGet() * purchase.price());
+            price += discountEligiblePrice;
+        }
+        return price;
+    }
+
+    public static List<Purchase> getPurchases() {
+        return receipts;
+    }
+}
\ No newline at end of file

리뷰: 해당 연산은 따로 메서드로 분리하면 어떨까요?
`purchase`를 받아 연산한 결과를 반환하면 좋을 것 같아요
@@ -0,0 +1,16 @@
+package config;
+
+public enum PathConfig {
+    PRODUCTS_FILE_PATH("src/main/resources/products.md"),
+    PROMOTIONS_FILE_PATH("src/main/resources/promotions.md");
+
+    private final String filePath;
+
+    PathConfig(final String filePath) {
+        this.filePath = filePath;
+    }
+
+    public final String getFilePath() {
+        return this.filePath;
+    }
+}
\ No newline at end of file

리뷰: 파일을 enum클래스로 만들어서 관리하는거 좋은거 같아요!
@@ -0,0 +1,24 @@
+package controller;
+
+import java.util.Objects;
+import model.Membership;
+import model.Receipts;
+import view.InputView;
+
+public final class MembershipController {
+    public static final int MAX_LIMIT = 8000;
+
+    public static void membership() {
+        String input = InputView.membershipDiscount();
+        if (Objects.equals(input, "Y")){
+            Membership.setMembershipDiscount(membershipCalculate());
+            return;
+        }
+        Membership.setMembershipDiscount(0);
+    }
+
+    public static int membershipCalculate() {
+        int discountPrice = (Receipts.membershipDiscountEligiblePrice() * 30) / 100;
+        return Math.min(discountPrice, MAX_LIMIT);
+    }
+}

리뷰: 저도 잘은 모르지만 계산하는 로직은 컨트롤러에 있는것보다 다른 클래스로 분리하는게 컨트롤러 기능에 맞지 않을까 싶어서 적어봅니다..!
@@ -0,0 +1,23 @@
+package controller;
+
+import validation.InputOrderValidation;
+import view.InputView;
+import view.OutputView;
+
+public final class OrderController {
+    public static void order() {
+        while (true) {
+            try {
+                orderInput();
+                return;
+            } catch (IllegalArgumentException error) {
+                OutputView.outputErrorMessage(error);
+            }
+        }
+    }
+
+    private static void orderInput() throws IllegalArgumentException {
+        String inputOrder = InputView.inputOrder();
+        InputOrderValidation.inputOrderValidation(inputOrder);
+    }
+}

리뷰: 무한반복을 돌리는것보다 횟수제한을 둔다던가 하는 방식이 코드 기능면이나 사용자 측면에서 더 좋을거같다는 피드백을 저도 받아서 적어봅니다!
@@ -0,0 +1,35 @@
+package controller;
+
+import config.PathConfig;
+import dto.ProductDTO;
+import java.util.List;
+import model.Inventory;
+import model.Promotion;
+import model.Promotions;
+import util.FileUtil;
+import util.SplitUtil;
+
+public final class LoadDataController {
+    public static void loadData() {
+        promotionsData();
+        productsData();
+    }
+
+    public static void productsData() {
+        List<String> fileContents = FileUtil.readFile(PathConfig.PRODUCTS_FILE_PATH.getFilePath());
+        for(String content: fileContents) {
+            ProductDTO productDTO = SplitUtil.createProductDTOFromContent(content);
+            Inventory.productAdd(productDTO);
+        }
+    }
+
+    public static void promotionsData() {
+        List<String> fileContents = FileUtil.readFile(PathConfig.PROMOTIONS_FILE_PATH.getFilePath());
+        fileContents.stream()
+                .map(SplitUtil::createPromotionFromContent)
+                // 처음 promotions.promotion에 현재 기간에 행사 진행하는 것만 넣을 건지 다 넣을 건지 filter
+                .filter(promotion -> Promotion.currentPromotion(promotion.startTime(), promotion.endTime()))
+                .forEach(Promotions::add);
+    }
+}
+

리뷰: loadPromotionsData 이렇게 말인가요?
@@ -0,0 +1,24 @@
+package controller;
+
+import java.util.Objects;
+import model.Membership;
+import model.Receipts;
+import view.InputView;
+
+public final class MembershipController {
+    public static final int MAX_LIMIT = 8000;
+
+    public static void membership() {
+        String input = InputView.membershipDiscount();
+        if (Objects.equals(input, "Y")){
+            Membership.setMembershipDiscount(membershipCalculate());
+            return;
+        }
+        Membership.setMembershipDiscount(0);
+    }
+
+    public static int membershipCalculate() {
+        int discountPrice = (Receipts.membershipDiscountEligiblePrice() * 30) / 100;
+        return Math.min(discountPrice, MAX_LIMIT);
+    }
+}

리뷰: model.Membership 클래스로 옮겨도 좋을 것 같네요.
@@ -0,0 +1,32 @@
+package model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class Receipts {
+    private static final List<Purchase> receipts = new ArrayList<>();
+
+    private Receipts() {}
+
+    public static void clear() {
+        receipts.clear();
+    }
+
+    public static void add(Purchase purchase) {
+        receipts.add(purchase);
+    }
+
+    public static int membershipDiscountEligiblePrice() {
+        int price = 0;
+        for (Purchase purchase : receipts) {
+            int discountEligiblePrice = (purchase.price() * purchase.quantity())
+                    - (purchase.promotionQuantity() * purchase.buyPlusGet() * purchase.price());
+            price += discountEligiblePrice;
+        }
+        return price;
+    }
+
+    public static List<Purchase> getPurchases() {
+        return receipts;
+    }
+}
\ No newline at end of file

리뷰: 말씀하신대로 해당 연산을 Purchase 클래스로 분할하면 더 좋을 것 같네요
@@ -0,0 +1,24 @@
+package controller;
+
+import java.util.Objects;
+import model.Membership;
+import model.Receipts;
+import view.InputView;
+
+public final class MembershipController {
+    public static final int MAX_LIMIT = 8000;
+
+    public static void membership() {
+        String input = InputView.membershipDiscount();
+        if (Objects.equals(input, "Y")){
+            Membership.setMembershipDiscount(membershipCalculate());
+            return;
+        }
+        Membership.setMembershipDiscount(0);
+    }
+
+    public static int membershipCalculate() {
+        int discountPrice = (Receipts.membershipDiscountEligiblePrice() * 30) / 100;
+        return Math.min(discountPrice, MAX_LIMIT);
+    }
+}

리뷰: 상수 처리 하면 더 좋은 코드가 될 것 같아요~ 감사합니다.
@@ -0,0 +1,67 @@
+package subway.controller;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import subway.command.Command;
+import subway.command.MainCommand;
+import subway.controller.handler.Handler;
+import subway.controller.handler.LineHandler;
+import subway.controller.handler.SectionHandler;
+import subway.controller.handler.StationHandler;
+import subway.controller.retryInputUtil.RetryInputUtil;
+import subway.dto.LineDto;
+import subway.service.LineService;
+import subway.service.SectionService;
+import subway.service.StationService;
+import subway.view.OutputView;
+
+public class SubwayController {
+    private final LineService lineService;
+    private final Map<MainCommand, Handler> commandHandlers;
+
+    public SubwayController(StationService stationService, LineService lineService, SectionService sectionService) {
+        this.lineService = lineService;
+
+        this.commandHandlers = new HashMap<>();
+        this.commandHandlers.put(MainCommand.STATION, new StationHandler(stationService));
+        this.commandHandlers.put(MainCommand.LINE, new LineHandler(lineService));
+        this.commandHandlers.put(MainCommand.SECTION, new SectionHandler(sectionService));
+    }
+
+    public void run() {
+        int state = 0;
+
+        while (state == 0) {
+            try {
+                OutputView.printMainMenu();
+                MainCommand mainCommand = Command.findCommand(RetryInputUtil.getMainCommand(), MainCommand.values());
+
+                state = mainLogic(mainCommand);
+            } catch (IllegalArgumentException error) {
+                OutputView.printError(error.getMessage());
+            }
+        }
+    }
+
+    private int mainLogic(MainCommand mainCommand) {
+        if (mainCommand == MainCommand.QUIT) {
+            return 1;
+        }
+
+        if (this.commandHandlers.containsKey(mainCommand)) {
+            this.commandHandlers.get(mainCommand).handle();
+        }
+        if (mainCommand == MainCommand.LINE_PRINT) {
+            printLineMap();
+        }
+
+        return 0;
+    }
+
+    private void printLineMap() {
+        List<LineDto> lines = lineService.retrieve().lines();
+        OutputView.printLineMap(lines);
+    }
+
+}

리뷰: c에서는 뒤늦게 bool이 들어와서  어쩔 수 없지만 자바에는 boolean이 있으니 boolean을 쓰는게 더 직관적인것 같습니당
@@ -0,0 +1,54 @@
+package subway.controller.handler;
+
+import java.util.List;
+import subway.command.Command;
+import subway.command.StationCommand;
+import subway.controller.retryInputUtil.StationRetryInput;
+import subway.dto.StationDto;
+import subway.dto.StationRegisterDto.StationRegisterInputDto;
+import subway.dto.StationRemoveDto.StationRemoveInputDto;
+import subway.service.StationService;
+import subway.view.OutputView;
+
+public class StationHandler implements Handler {
+    private final StationService stationService;
+
+    public StationHandler(StationService stationService) {
+        this.stationService = stationService;
+    }
+
+    @Override
+    public void handle() {
+        OutputView.printStationManageMenu();
+        StationCommand stationCommand = Command.findCommand(StationRetryInput.getCommand(), StationCommand.values());
+
+        if (stationCommand == StationCommand.REGISTER) {
+            registerStation();
+        }
+        if (stationCommand == StationCommand.REMOVE) {
+            removeStation();
+        }
+        if (stationCommand == StationCommand.RETRIEVE) {
+            retrieveStation();
+        }
+        if (stationCommand == StationCommand.BACK) {
+            // nothing to do.
+        }
+    }
+
+    private void registerStation() {
+        String stationName = StationRetryInput.getStationName();
+        stationService.register(new StationRegisterInputDto(stationName));
+    }
+
+    private void removeStation() {
+        String stationName = StationRetryInput.getRemoveStationName();
+        stationService.remove(new StationRemoveInputDto(stationName));
+        OutputView.printStationRemovedMessage();
+    }
+
+    private void retrieveStation() {
+        List<StationDto> stations = stationService.retrieve().stations();
+        OutputView.printStations(stations);
+    }
+}

리뷰: 이부분을 지우고 상단의 if문 2개에 return을 넣으면 코드수를 줄일 수 있을 것 같네요!
@@ -0,0 +1,52 @@
+package subway.config;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import subway.controller.SubwayController;
+import subway.repository.LineRepository;
+import subway.repository.StationRepository;
+import subway.service.LineService;
+import subway.service.SectionService;
+import subway.service.StationService;
+import subway.utils.CsvReader;
+import subway.utils.LineParser;
+import subway.utils.StationParser;
+
+public class DependencyInjector {
+    public SubwayController createSubwayController() {
+        StationRepository stationRepository = new StationRepository();
+        LineRepository lineRepository = new LineRepository();
+
+        StationService stationService = new StationService(stationRepository);
+        SectionService sectionService = new SectionService(lineRepository, stationRepository);
+        LineService lineService = new LineService(lineRepository, stationRepository);
+
+        SubwayFileInitializer subwayFileInitializer = createSubwayFileInitializer(stationRepository, lineRepository,
+                stationService, sectionService);
+        subwayFileInitializer.init();
+
+        return new SubwayController(stationService, lineService, sectionService);
+    }
+
+    public SubwayFileInitializer createSubwayFileInitializer(StationRepository stationRepository,
+                                                             LineRepository lineRepository,
+                                                             StationService stationService,
+                                                             SectionService sectionService) {
+        StationParser stationParser = new StationParser(
+                new CsvReader(this.createBufferedReader(Configuration.STATION_FILE_NAME.getString()),
+                        false));
+        LineParser lineParser = new LineParser(
+                new CsvReader(this.createBufferedReader(Configuration.LINE_FILE_NAME.getString()), false));
+
+        return new SubwayFileInitializer(
+                stationParser, lineParser, stationRepository, lineRepository
+        );
+    }
+
+
+    private BufferedReader createBufferedReader(String fileName) {
+        return new BufferedReader(new InputStreamReader(
+                DependencyInjector.class.getClassLoader().getResourceAsStream(fileName)
+        ));
+    }
+}

리뷰: 이 메서드를 나눌 수 있지 않았나 싶습니다. 여기서 new로 새로 할당한 개체들을 싱글톤으로 만들었으면 어떨까요?
@@ -0,0 +1,16 @@
+package subway.command;
+
+public interface Command {
+    static <T extends Command> T findCommand(String input, T[] commands) {
+        for (T command : commands) {
+            if (command.getCommand().equals(input)) {
+                return command;
+            }
+        }
+        throw new IllegalArgumentException("선택할 수 없는 기능입니다.");
+    }
+
+    String getCommand();
+
+    String getDescription();
+}

리뷰: 기능 선택을 인터페이스로 관리하는 방법도 있군요.
로직이 반복되는 것 같아 고민했는데 좋은 방법이네요! 
이렇게 하면 controller에서 해당 static 메소드를 호출하면 되니 역할 분리도 챙길 수 있네요.
배워갑니다👍
@@ -0,0 +1,16 @@
+package subway.command;
+
+public interface Command {
+    static <T extends Command> T findCommand(String input, T[] commands) {
+        for (T command : commands) {
+            if (command.getCommand().equals(input)) {
+                return command;
+            }
+        }
+        throw new IllegalArgumentException("선택할 수 없는 기능입니다.");
+    }
+
+    String getCommand();
+
+    String getDescription();
+}

리뷰: 에러메세지를 따로 관리하지 않고 직접 작성하신 이유가 있으실까요?
@@ -0,0 +1,27 @@
+package subway.command;
+
+public enum LineCommand implements Command {
+    REGISTER("1", "노선 등록"),
+    REMOVE("2", "노선 삭제"),
+    RETRIEVE("3", "노선 조회"),
+    BACK("B", "돌아가기");
+
+    private final String command;
+    private final String description;
+
+    LineCommand(String command, String description) {
+        this.command = command;
+        this.description = description;
+    }
+
+    @Override
+    public String getCommand() {
+        return command;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+}

리뷰: 저는 enum에서 "1", "2" 라는 선택지 번호(또는 문자)만 관리했었는데, 출력 문구까지 작성하는게 더 깔끔하네요.
@@ -0,0 +1,24 @@
+package subway.config;
+
+public enum Configuration {
+    STATION_FILE_NAME("stations.md"),
+    LINE_FILE_NAME("lines.md"),
+    LINE_NAME_MIN_LENGTH(2),
+    STATION_NAME_MIN_LENGTH(2),
+    ORDER_MIN_NUMBER(1),
+    ;
+
+    private final Object value;
+
+    Configuration(Object value) {
+        this.value = value;
+    }
+
+    public int getInt() {
+        return (int) value;
+    }
+
+    public String getString() {
+        return (String) value;
+    }
+}

리뷰: '순서는 최소 1이어야 한다'인 것 같은데 맞을까요?
저는 설명 중에 구간에 대해 '역과 역 사이'라는 설명이 있어서 구간 등록 시 최소 2로 이해했었는데, 어떻게 생각하느냐에 따라 다를 수 있을 것 같아요.

아래는 제가 이해한 구간에 대한 예시입니다.

1호선 : ㅇㅇ역 - ㅁㅁ역 - ㅂㅂ역
- 구간 1 : ㅇㅇ역과 ㅁㅁ역 사이
- 구간 2 : ㅁㅁ역과 ㅂㅂ역 사이
@@ -0,0 +1,52 @@
+package subway.config;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import subway.controller.SubwayController;
+import subway.repository.LineRepository;
+import subway.repository.StationRepository;
+import subway.service.LineService;
+import subway.service.SectionService;
+import subway.service.StationService;
+import subway.utils.CsvReader;
+import subway.utils.LineParser;
+import subway.utils.StationParser;
+
+public class DependencyInjector {
+    public SubwayController createSubwayController() {
+        StationRepository stationRepository = new StationRepository();
+        LineRepository lineRepository = new LineRepository();
+
+        StationService stationService = new StationService(stationRepository);
+        SectionService sectionService = new SectionService(lineRepository, stationRepository);
+        LineService lineService = new LineService(lineRepository, stationRepository);
+
+        SubwayFileInitializer subwayFileInitializer = createSubwayFileInitializer(stationRepository, lineRepository,
+                stationService, sectionService);
+        subwayFileInitializer.init();
+
+        return new SubwayController(stationService, lineService, sectionService);
+    }
+
+    public SubwayFileInitializer createSubwayFileInitializer(StationRepository stationRepository,
+                                                             LineRepository lineRepository,
+                                                             StationService stationService,
+                                                             SectionService sectionService) {
+        StationParser stationParser = new StationParser(
+                new CsvReader(this.createBufferedReader(Configuration.STATION_FILE_NAME.getString()),
+                        false));
+        LineParser lineParser = new LineParser(
+                new CsvReader(this.createBufferedReader(Configuration.LINE_FILE_NAME.getString()), false));
+
+        return new SubwayFileInitializer(
+                stationParser, lineParser, stationRepository, lineRepository
+        );
+    }
+
+
+    private BufferedReader createBufferedReader(String fileName) {
+        return new BufferedReader(new InputStreamReader(
+                DependencyInjector.class.getClassLoader().getResourceAsStream(fileName)
+        ));
+    }
+}

리뷰: BufferedReader를 이렇게 처리할 수도 있군요👍
@@ -0,0 +1,78 @@
+package subway.config;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import subway.domain.Line;
+import subway.domain.Station;
+import subway.exception.ReaderException;
+import subway.repository.LineRepository;
+import subway.repository.StationRepository;
+import subway.utils.LineFieldsDto;
+import subway.utils.LineParser;
+import subway.utils.StationFieldsDto;
+import subway.utils.StationParser;
+
+public class SubwayFileInitializer {
+    private final StationParser stationParser;
+    private final LineParser lineParser;
+    private final StationRepository stationRepository;
+    private final LineRepository lineRepository;
+
+    public SubwayFileInitializer(StationParser stationParser, LineParser lineParser,
+                                 StationRepository stationRepository, LineRepository lineRepository) {
+        this.stationParser = stationParser;
+        this.lineParser = lineParser;
+        this.stationRepository = stationRepository;
+        this.lineRepository = lineRepository;
+    }
+
+    public void init() {
+        try {
+            initStations();
+            initLines();
+        } catch (IOException e) {
+            throw new ReaderException();
+        } catch (IllegalArgumentException error) {
+            throw new IllegalArgumentException(error.getMessage());
+        }
+    }
+
+    private void initStations() throws IOException {
+        while (true) {
+            StationFieldsDto stationFieldsDto = stationParser.nextStation();
+            if (stationFieldsDto == null) {
+                break;
+            }
+
+            stationRepository.add(new Station(stationFieldsDto.stationName()));
+        }
+    }
+
+    private void initLines() throws IOException {
+        while (true) {
+            LineFieldsDto lineFieldsDto = lineParser.nextLine();
+            if (lineFieldsDto == null) {
+                break;
+            }
+
+            List<Station> stations = new ArrayList<>();
+            for (String stationName : lineFieldsDto.stationNames()) {
+                Station station = stationRepository.findByName(stationName);
+                if (station == null) {
+                    throw new IllegalArgumentException();
+                }
+
+                stations.add(station);
+            }
+
+            Line line = new Line(lineFieldsDto.lineName(), stations.getFirst(), stations.getLast());
+            lineRepository.add(line);
+            for (int i = 1; i < stations.size() - 1; i++) {
+                Station station = stations.get(i);
+                lineRepository.addStation(line, station, i);
+            }
+        }
+    }
+
+}

리뷰: 해당 메소드의 길이가 15 이상인 것 같습니다!
미션의 프로그래밍 요구사항 중 메소드 길이가 15 이하여야 한다는 조건이 있었습니당
@@ -0,0 +1,67 @@
+package subway.controller;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import subway.command.Command;
+import subway.command.MainCommand;
+import subway.controller.handler.Handler;
+import subway.controller.handler.LineHandler;
+import subway.controller.handler.SectionHandler;
+import subway.controller.handler.StationHandler;
+import subway.controller.retryInputUtil.RetryInputUtil;
+import subway.dto.LineDto;
+import subway.service.LineService;
+import subway.service.SectionService;
+import subway.service.StationService;
+import subway.view.OutputView;
+
+public class SubwayController {
+    private final LineService lineService;
+    private final Map<MainCommand, Handler> commandHandlers;
+
+    public SubwayController(StationService stationService, LineService lineService, SectionService sectionService) {
+        this.lineService = lineService;
+
+        this.commandHandlers = new HashMap<>();
+        this.commandHandlers.put(MainCommand.STATION, new StationHandler(stationService));
+        this.commandHandlers.put(MainCommand.LINE, new LineHandler(lineService));
+        this.commandHandlers.put(MainCommand.SECTION, new SectionHandler(sectionService));
+    }
+
+    public void run() {
+        int state = 0;
+
+        while (state == 0) {
+            try {
+                OutputView.printMainMenu();
+                MainCommand mainCommand = Command.findCommand(RetryInputUtil.getMainCommand(), MainCommand.values());
+
+                state = mainLogic(mainCommand);
+            } catch (IllegalArgumentException error) {
+                OutputView.printError(error.getMessage());
+            }
+        }
+    }
+
+    private int mainLogic(MainCommand mainCommand) {
+        if (mainCommand == MainCommand.QUIT) {
+            return 1;
+        }
+
+        if (this.commandHandlers.containsKey(mainCommand)) {
+            this.commandHandlers.get(mainCommand).handle();
+        }
+        if (mainCommand == MainCommand.LINE_PRINT) {
+            printLineMap();
+        }
+
+        return 0;
+    }
+
+    private void printLineMap() {
+        List<LineDto> lines = lineService.retrieve().lines();
+        OutputView.printLineMap(lines);
+    }
+
+}

리뷰: 깔끔한 구조네요👍