ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -1,37 +1,20 @@
 import java.util.List;
+import java.util.Set;
 
-import domain.LottoGame;
-import domain.LottoResults;
-import domain.WinningAnalyzer;
-import domain.WinningStatistics;
+import domain.ManualRequest;
 import view.LottoInputView;
-import view.LottoOutputView;
 
 public class LottoController {
-    private LottoGame lottoGame;
-    private WinningAnalyzer winningAnalyzer;
 
-    public void playLottoGames(int money) {
-        lottoGame = new LottoGame();
-        lottoGame.generateLottoResultsFromMoney(money);
-    }
-
-    public void getLottoResults() {
-        List<List<Integer>> results = lottoGame.getLottoResults().lottoNumbersToInt();
-        int gameCount = lottoGame.getCount();
-        LottoOutputView.printLottoResults(gameCount, results);
-    }
-
-    public void getWinningStatistics() {
-        LottoResults lottoResults = lottoGame.getLottoResults();
-        winningAnalyzer = new WinningAnalyzer(lottoResults, LottoInputView.getWinningNumbers(), LottoInputView.getBonusNumber());
-        WinningStatistics winningStatistics = winningAnalyzer.calculateWinningStatistics();
-        LottoOutputView.printBeforeWinnings();
-        LottoOutputView.printWinnings(winningStatistics);
-    }
-
-    public void getReturnOnInvestment() {
-        int money = lottoGame.getMoney();
-        LottoOutputView.printReturnOnInvestment(winningAnalyzer.getReturnOnInvestment(money));
+    public static void main(String[] args) {
+        int money = LottoInputView.getMoney();
+        int manualCount = LottoInputView.getManualCount();
+        List<Set<Integer>> manualNumbers = LottoInputView.getManualLottos(manualCount);
+        LottoService lottoService = new LottoService();
+        ManualRequest manualRequest = new ManualRequest(manualCount, manualNumbers);
+        lottoService.playLottoGames(manualRequest, money);
+        lottoService.lottoResults();
+        lottoService.winningStatistics();
+        lottoService.returnOnInvestment();
     }
 }

리뷰: 일급 컬렉션을 써보면 어떨까요?
@@ -1,13 +0,0 @@
-import view.LottoInputView;
-
-public class LottoMain {
-
-    public static void main(String[] args) {
-        int money = LottoInputView.getMoney();
-        LottoController lottoController = new LottoController();
-        lottoController.playLottoGames(money);
-        lottoController.getLottoResults();
-        lottoController.getWinningStatistics();
-        lottoController.getReturnOnInvestment();
-    }
-}

리뷰: - 값 대입, 메서드 호출과 같이 개념적 유사성이 다른 코드들간에는 개행으로 가독성을 높혀줄 수 있습니다. 
- 컨트롤러에게 세부 기능들을 호출하고 있습니다. 이건 비즈니스로직 오케스트레이션으로 보여요. 그냥 적절한 Request 객체를 전달하고 최종 결과만 반환받아서 출력계층에게 전달하면 될 것 같아요. 
- 현재 구조를 보면 LottoMain이 컨트롤러의 역할을 하고 있고, LottoController가 Service Layer의 역할을 하고 있습니다. 그럼 차라리 Controller에서 View Layer를 분리해 LottoMain으로 옮기시고 LottoController의 이름을 LottoService로 바꾸는건 어떨까요
@@ -0,0 +1,15 @@
+package domain;
+
+public class GameCount {
+    private final int manualCount;
+    private final int automaticCount;
+
+    public GameCount(int totalCount, int manualCount) {
+        this.manualCount = manualCount;
+        this.automaticCount = totalCount - manualCount;
+    }
+
+    public int getAutomaticCount() {
+        return automaticCount;
+    }
+}

리뷰: 내부적으로 값이 변경되지 않는다면 final 키워드를 통해 불변성 확보를 할 수 있을 것 같네요.
@@ -1,23 +1,47 @@
 package domain;
 
+import java.util.List;
+import java.util.Set;
+
 public class LottoGame {
     private LottoResults lottoResults;
-
     private Money money;
+    private WinningAnalyzer winningAnalyzer;
 
     public LottoGame() {
         this.lottoResults = new LottoResults();
     }
 
-    public void generateLottoResultsFromMoney(int money) {
-        this.money = new Money(money);
-        for (int i = 0; i < getCount(); i++) {
-            lottoResults.add(LottoNumGenerator.generateResult());
+    public void generateLottoResultsFromMoney(ManualRequest manualRequest, int money) {
+        int manualCount = manualRequest.getManualCount();
+        List<Set<Integer>> manualNumbers = manualRequest.getManualNumbers();
+        this.money = new Money(money, manualCount);
+        if (manualCount != manualNumbers.size()) {
+            throw new IllegalArgumentException("수동 구매가 잘못되었습니다.");
+        }
+        // 수동 구매
+        generateManualResults(manualCount, manualNumbers);
+        generateAutomaticResults();
+    }
+
+    private void generateAutomaticResults() {
+        for (int i = 0; i < getAutomaticCount(); i++) {
+            lottoResults.add(LottoNumGenerator.generateAutomaticResults());
+        }
+    }
+
+    private void generateManualResults(int manualCount, List<Set<Integer>> manualNumbers) {
+        for (int i = 0; i < manualCount; i++) {
+            lottoResults.add(LottoNumGenerator.generateManualResults(manualNumbers.get(i)));
         }
     }
 
     public int getCount() {
-        return money.getCount();
+        return money.getTotalCount();
+    }
+
+    public int getAutomaticCount() {
+        return money.getAutomaticCount();
     }
 
     public int getMoney() {
@@ -27,5 +51,19 @@ public int getMoney() {
     public LottoResults getLottoResults() {
         return lottoResults;
     }
+
+    public List<List<Integer>> getLottoResultsToInt() {
+        return lottoResults.lottoResultsToInt();
+    }
+
+    public WinningStatistics calculateWinningStatistics(Set<Integer> winningNumbers, int bonusNumber) {
+        winningAnalyzer = new WinningAnalyzer(lottoResults, winningNumbers, bonusNumber);
+        return winningAnalyzer.calculateWinningStatistics();
+    }
+
+    public float getReturnOnInvestment() {
+        return winningAnalyzer.getReturnOnInvestment(money.getMoney());
+    }
+
 }
 

리뷰: 이런 참조타입도 일급 컬렉션을 이용해서 내부 인덱스 제어를 책임 분리할 수 있을 것 같아요.
@@ -1,23 +1,47 @@
 package domain;
 
+import java.util.List;
+import java.util.Set;
+
 public class LottoGame {
     private LottoResults lottoResults;
-
     private Money money;
+    private WinningAnalyzer winningAnalyzer;
 
     public LottoGame() {
         this.lottoResults = new LottoResults();
     }
 
-    public void generateLottoResultsFromMoney(int money) {
-        this.money = new Money(money);
-        for (int i = 0; i < getCount(); i++) {
-            lottoResults.add(LottoNumGenerator.generateResult());
+    public void generateLottoResultsFromMoney(ManualRequest manualRequest, int money) {
+        int manualCount = manualRequest.getManualCount();
+        List<Set<Integer>> manualNumbers = manualRequest.getManualNumbers();
+        this.money = new Money(money, manualCount);
+        if (manualCount != manualNumbers.size()) {
+            throw new IllegalArgumentException("수동 구매가 잘못되었습니다.");
+        }
+        // 수동 구매
+        generateManualResults(manualCount, manualNumbers);
+        generateAutomaticResults();
+    }
+
+    private void generateAutomaticResults() {
+        for (int i = 0; i < getAutomaticCount(); i++) {
+            lottoResults.add(LottoNumGenerator.generateAutomaticResults());
+        }
+    }
+
+    private void generateManualResults(int manualCount, List<Set<Integer>> manualNumbers) {
+        for (int i = 0; i < manualCount; i++) {
+            lottoResults.add(LottoNumGenerator.generateManualResults(manualNumbers.get(i)));
         }
     }
 
     public int getCount() {
-        return money.getCount();
+        return money.getTotalCount();
+    }
+
+    public int getAutomaticCount() {
+        return money.getAutomaticCount();
     }
 
     public int getMoney() {
@@ -27,5 +51,19 @@ public int getMoney() {
     public LottoResults getLottoResults() {
         return lottoResults;
     }
+
+    public List<List<Integer>> getLottoResultsToInt() {
+        return lottoResults.lottoResultsToInt();
+    }
+
+    public WinningStatistics calculateWinningStatistics(Set<Integer> winningNumbers, int bonusNumber) {
+        winningAnalyzer = new WinningAnalyzer(lottoResults, winningNumbers, bonusNumber);
+        return winningAnalyzer.calculateWinningStatistics();
+    }
+
+    public float getReturnOnInvestment() {
+        return winningAnalyzer.getReturnOnInvestment(money.getMoney());
+    }
+
 }
 

리뷰: Money객체인데 count정보까지 담아버리면 일급 객체로써의 효용이 떨어지고 객체 시그니처가 불분명해질 것 같네요 🤔
@@ -1,23 +1,47 @@
 package domain;
 
+import java.util.List;
+import java.util.Set;
+
 public class LottoGame {
     private LottoResults lottoResults;
-
     private Money money;
+    private WinningAnalyzer winningAnalyzer;
 
     public LottoGame() {
         this.lottoResults = new LottoResults();
     }
 
-    public void generateLottoResultsFromMoney(int money) {
-        this.money = new Money(money);
-        for (int i = 0; i < getCount(); i++) {
-            lottoResults.add(LottoNumGenerator.generateResult());
+    public void generateLottoResultsFromMoney(ManualRequest manualRequest, int money) {
+        int manualCount = manualRequest.getManualCount();
+        List<Set<Integer>> manualNumbers = manualRequest.getManualNumbers();
+        this.money = new Money(money, manualCount);
+        if (manualCount != manualNumbers.size()) {
+            throw new IllegalArgumentException("수동 구매가 잘못되었습니다.");
+        }
+        // 수동 구매
+        generateManualResults(manualCount, manualNumbers);
+        generateAutomaticResults();
+    }
+
+    private void generateAutomaticResults() {
+        for (int i = 0; i < getAutomaticCount(); i++) {
+            lottoResults.add(LottoNumGenerator.generateAutomaticResults());
+        }
+    }
+
+    private void generateManualResults(int manualCount, List<Set<Integer>> manualNumbers) {
+        for (int i = 0; i < manualCount; i++) {
+            lottoResults.add(LottoNumGenerator.generateManualResults(manualNumbers.get(i)));
         }
     }
 
     public int getCount() {
-        return money.getCount();
+        return money.getTotalCount();
+    }
+
+    public int getAutomaticCount() {
+        return money.getAutomaticCount();
     }
 
     public int getMoney() {
@@ -27,5 +51,19 @@ public int getMoney() {
     public LottoResults getLottoResults() {
         return lottoResults;
     }
+
+    public List<List<Integer>> getLottoResultsToInt() {
+        return lottoResults.lottoResultsToInt();
+    }
+
+    public WinningStatistics calculateWinningStatistics(Set<Integer> winningNumbers, int bonusNumber) {
+        winningAnalyzer = new WinningAnalyzer(lottoResults, winningNumbers, bonusNumber);
+        return winningAnalyzer.calculateWinningStatistics();
+    }
+
+    public float getReturnOnInvestment() {
+        return winningAnalyzer.getReturnOnInvestment(money.getMoney());
+    }
+
 }
 

리뷰: 자동,수동 로또면 그냥 LottoNumber, Lotto, Lottos 정도로 네이밍을 지어도 될 것 같네요 Result라고하니 뭔가 비교까지 다 끝난 최종 결과를 의미하는 걸로 보입니다.
@@ -1,23 +1,47 @@
 package domain;
 
+import java.util.List;
+import java.util.Set;
+
 public class LottoGame {
     private LottoResults lottoResults;
-
     private Money money;
+    private WinningAnalyzer winningAnalyzer;
 
     public LottoGame() {
         this.lottoResults = new LottoResults();
     }
 
-    public void generateLottoResultsFromMoney(int money) {
-        this.money = new Money(money);
-        for (int i = 0; i < getCount(); i++) {
-            lottoResults.add(LottoNumGenerator.generateResult());
+    public void generateLottoResultsFromMoney(ManualRequest manualRequest, int money) {
+        int manualCount = manualRequest.getManualCount();
+        List<Set<Integer>> manualNumbers = manualRequest.getManualNumbers();
+        this.money = new Money(money, manualCount);
+        if (manualCount != manualNumbers.size()) {
+            throw new IllegalArgumentException("수동 구매가 잘못되었습니다.");
+        }
+        // 수동 구매
+        generateManualResults(manualCount, manualNumbers);
+        generateAutomaticResults();
+    }
+
+    private void generateAutomaticResults() {
+        for (int i = 0; i < getAutomaticCount(); i++) {
+            lottoResults.add(LottoNumGenerator.generateAutomaticResults());
+        }
+    }
+
+    private void generateManualResults(int manualCount, List<Set<Integer>> manualNumbers) {
+        for (int i = 0; i < manualCount; i++) {
+            lottoResults.add(LottoNumGenerator.generateManualResults(manualNumbers.get(i)));
         }
     }
 
     public int getCount() {
-        return money.getCount();
+        return money.getTotalCount();
+    }
+
+    public int getAutomaticCount() {
+        return money.getAutomaticCount();
     }
 
     public int getMoney() {
@@ -27,5 +51,19 @@ public int getMoney() {
     public LottoResults getLottoResults() {
         return lottoResults;
     }
+
+    public List<List<Integer>> getLottoResultsToInt() {
+        return lottoResults.lottoResultsToInt();
+    }
+
+    public WinningStatistics calculateWinningStatistics(Set<Integer> winningNumbers, int bonusNumber) {
+        winningAnalyzer = new WinningAnalyzer(lottoResults, winningNumbers, bonusNumber);
+        return winningAnalyzer.calculateWinningStatistics();
+    }
+
+    public float getReturnOnInvestment() {
+        return winningAnalyzer.getReturnOnInvestment(money.getMoney());
+    }
+
 }
 

리뷰: 자동생성과 수동생성을 메서드로 구분하고 있는데 LottoGenerator -> LottoAutoGenerator, LottoManualGenerator 등으로 전략패턴을 사용할수도 있을 것 같네요
@@ -1,23 +1,47 @@
 package domain;
 
+import java.util.List;
+import java.util.Set;
+
 public class LottoGame {
     private LottoResults lottoResults;
-
     private Money money;
+    private WinningAnalyzer winningAnalyzer;
 
     public LottoGame() {
         this.lottoResults = new LottoResults();
     }
 
-    public void generateLottoResultsFromMoney(int money) {
-        this.money = new Money(money);
-        for (int i = 0; i < getCount(); i++) {
-            lottoResults.add(LottoNumGenerator.generateResult());
+    public void generateLottoResultsFromMoney(ManualRequest manualRequest, int money) {
+        int manualCount = manualRequest.getManualCount();
+        List<Set<Integer>> manualNumbers = manualRequest.getManualNumbers();
+        this.money = new Money(money, manualCount);
+        if (manualCount != manualNumbers.size()) {
+            throw new IllegalArgumentException("수동 구매가 잘못되었습니다.");
+        }
+        // 수동 구매
+        generateManualResults(manualCount, manualNumbers);
+        generateAutomaticResults();
+    }
+
+    private void generateAutomaticResults() {
+        for (int i = 0; i < getAutomaticCount(); i++) {
+            lottoResults.add(LottoNumGenerator.generateAutomaticResults());
+        }
+    }
+
+    private void generateManualResults(int manualCount, List<Set<Integer>> manualNumbers) {
+        for (int i = 0; i < manualCount; i++) {
+            lottoResults.add(LottoNumGenerator.generateManualResults(manualNumbers.get(i)));
         }
     }
 
     public int getCount() {
-        return money.getCount();
+        return money.getTotalCount();
+    }
+
+    public int getAutomaticCount() {
+        return money.getAutomaticCount();
     }
 
     public int getMoney() {
@@ -27,5 +51,19 @@ public int getMoney() {
     public LottoResults getLottoResults() {
         return lottoResults;
     }
+
+    public List<List<Integer>> getLottoResultsToInt() {
+        return lottoResults.lottoResultsToInt();
+    }
+
+    public WinningStatistics calculateWinningStatistics(Set<Integer> winningNumbers, int bonusNumber) {
+        winningAnalyzer = new WinningAnalyzer(lottoResults, winningNumbers, bonusNumber);
+        return winningAnalyzer.calculateWinningStatistics();
+    }
+
+    public float getReturnOnInvestment() {
+        return winningAnalyzer.getReturnOnInvestment(money.getMoney());
+    }
+
 }
 

리뷰: 이 메서드도 ProfitCalculator -> DefaultProfiltCalculator등으로 분리해서 이율 계산도 추상화하면 유지보수성이 높아질 것 같아요
@@ -1,7 +1,9 @@
 package domain;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 public class LottoResult {
     private List<LottoNumber> lottoResult;
@@ -14,10 +16,11 @@ public LottoResult(List<LottoNumber> lottoNumbers) {
         this.lottoResult = lottoNumbers;
     }
 
-    public static LottoResult fromIntegers(List<Integer> lottoIntegers) {
+    public static LottoResult fromIntegers(Set<Integer> lottoIntegers) {
         LottoResult lottoResult = new LottoResult();
-        for (int i = 0; i < lottoIntegers.size(); i++) {
-            lottoResult.lottoResult.add(LottoNumber.from(lottoIntegers.get(i)));
+        Iterator<Integer> iterator = lottoIntegers.iterator();
+        while (iterator.hasNext()) {
+            lottoResult.lottoResult.add(LottoNumber.from(iterator.next()));
         }
         return lottoResult;
     }
@@ -38,4 +41,14 @@ public List<Integer> getLottoResult() {
         }
         return lottoNumbers;
     }
+
+    public int calculateCount(WinningNumbers winningNumbers) {
+        int count = 0;
+        return lottoResult.stream().mapToInt(num -> num.addCountIfContain(count, winningNumbers)).sum();
+    }
+
+    public boolean isBonusMatch(BonusNumber bonusNumber) {
+        return lottoResult.stream()
+                   .anyMatch(lottoNumber -> bonusNumber.isBonusMatch(lottoNumber));
+    }
 }

리뷰: 람다식에서 count라는 외부 값에 영향을 주고 있어요. 🤔

람다식은 외부값에 대한 영향을 주지 않아야 하기에 순수 함수로 설계되야합니다. 로직내에서 직접 count같은 지역변수를 변경하려고하면 effective final variable을 수정하려한다고 컴파일 에러가 발생할 것인데, 메서드를 다시 호출해서 그 문제를 피했지만, 그렇다고 문제가 되지 않는건 아닙니다. 

filter와 count등을 이용해서구현할수도 있을 것 같네요
@@ -2,25 +2,62 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.junit.jupiter.api.Test;
 
 public class LottoGameTest {
 
     @Test
-    public void 로또_구입금액을_입력하면_구입금액에_해당하는_로또를_발행한다() {
+    public void 로또_구입금액과_번호를_수동으로_입력하면_구입금액에_해당하는_로또를_발행한다() {
         //given
         int money = 14000;
+        Set<Integer> manualLottoNumber = new HashSet<>();
+        manualLottoNumber.add(2);
+        manualLottoNumber.add(4);
+        manualLottoNumber.add(6);
+        manualLottoNumber.add(7);
+        manualLottoNumber.add(10);
+        manualLottoNumber.add(12);
+
+        int manualCount = 1;
         LottoGame lottoGenerator = new LottoGame();
+        List<Set<Integer>> manualNumbers = new ArrayList<>();
+        manualNumbers.add(manualLottoNumber);
+        ManualRequest manualRequest = new ManualRequest(manualCount, manualNumbers);
+
         //when
-        lottoGenerator.generateLottoResultsFromMoney(money);
+        lottoGenerator.generateLottoResultsFromMoney(manualRequest, money);
         LottoResults lottoResults = lottoGenerator.getLottoResults();
-        List<List<Integer>> lottoResultList = lottoResults.lottoNumbersToInt();
+        List<List<Integer>> lottoResultList = lottoResults.lottoResultsToInt();
         //then
         assertThat(lottoResultList).hasSize(14);
         for (List<Integer> lottoNum : lottoResultList) {
             assertThat(lottoNum).hasSize(6);
         }
     }
+
+//    @Test
+//    public void 로또_번호를_수동으로_입력할_때_중복된_번호를_입력하면_예외를_던진다() {
+//        //given
+//        int money = 1000;
+//        List<Integer> manualLottoNumber = Arrays.asList(2, 2, 6, 7, 10, 12);
+//        int manualCount = 1;
+//        LottoGame lottoGenerator = new LottoGame();
+//        List<List<Integer>> manualNumbers = new ArrayList<>();
+//        manualNumbers.add(manualLottoNumber);
+//        ManualRequest manualRequest = new ManualRequest(manualCount, manualNumbers);
+//
+//        //when
+//
+//
+//        //then
+//
+//    }
+
+
+
 }

리뷰: 사용하지 않는 주석(코드)은 제거해주세요!
@@ -0,0 +1,96 @@
+const MAX_RANDOM_NUMBER = 9;
+const MIN_RANDOM_NUMBER = 1;
+const $inputNumber = document.querySelector('.input-number');
+const $gameTable = document.querySelector(".game-table");
+
+let answer;
+
+function inputDigit(numberOfDigit){
+    $gameTable.innerHTML = "<tr><td>순서</td><td>숫자</td><td>결과</td></tr>";
+    if(numberOfDigit === "3자리"){
+        answer = randomNumber(3);
+    } else if(numberOfDigit === "4자리"){
+        answer = randomNumber(4);
+    } else if(numberOfDigit === "5자리"){
+        answer = randomNumber(5);
+    }
+}
+
+function randomNumber(numberOfDigit){
+    let result = 0;
+    let existingNumbers = [];
+    for (step = 0; step < numberOfDigit; step++) {
+        let randomNumber = Math.floor(Math.random() * (MAX_RANDOM_NUMBER - MIN_RANDOM_NUMBER) + MIN_RANDOM_NUMBER);
+        while (existingNumbers.includes(randomNumber)){
+            randomNumber = Math.floor(Math.random() * (MAX_RANDOM_NUMBER - MIN_RANDOM_NUMBER) + MIN_RANDOM_NUMBER);
+        }
+        existingNumbers.push(randomNumber);
+    }
+
+    let weight = 1;
+    for(j = 0; j < numberOfDigit; j++){
+        result += existingNumbers[j] * weight;
+        weight *= 10;
+    }
+
+    console.log(result);
+    return result;
+}
+
+let index = 1;
+function addValue(){
+    const newRow = $gameTable.insertRow();
+    const pitchResult = checkBallAndStrike(answer);
+    const convertedAnswer = answer.toString();
+
+    let isValid = 0;
+    if($inputNumber.value.length === convertedAnswer.length){
+        for(let i = 0; i < $inputNumber.value.length; i++){
+            for(let j = 0; j < $inputNumber.value.length; j++){
+                if(i !== j){
+                    if($inputNumber.value[i] === $inputNumber.value[j]){
+                        isValid = 1;
+                    }
+                }
+            }
+        }
+        if(isValid === 0){
+            const turn = newRow.insertCell(0);
+            const number = newRow.insertCell(1);
+            const result = newRow.insertCell(2);
+            turn.innerText = `${index++}`;
+            number.innerText = `${$inputNumber.value}`
+            if(pitchResult[1] === convertedAnswer.length){
+                result.innerText = `O.K.`;
+            } else{
+                result.innerText = `B: ${pitchResult[0]}, S: ${pitchResult[1]}`;
+            }
+        } else{
+            alert("올바르지 않은 값입니다.");
+        }
+       
+    } else{
+        alert("올바르지 않은 값입니다.");
+    }
+
+}
+
+function checkBallAndStrike(answer){
+    const convertedAnswer = answer.toString();
+    const convertedInputNumber = $inputNumber.value.toString();
+    let k = 0;
+    let score = [0, 0];
+    while(k < convertedAnswer.length){
+        if(convertedAnswer[k] === convertedInputNumber[k]){
+            score[1] += 1;
+        } else{
+            for(let item of convertedAnswer){
+                if(item === convertedInputNumber[k]){
+                    score[0] += 1;
+                }
+            }
+        }
+        k += 1;
+    }
+    return score;
+}
\ No newline at end of file

리뷰: 서로 성격이 다른 변수 선언 간에는 줄바꿈을 한 번 넣어주는 게 더 보기 좋지 않을까요??🤔