Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
823effa
docs(readme): 사전 정보 전처리와 관련한 구현 목록 작성
Dec 4, 2022
a8c1848
feat(crew): 크루 목록이 담긴 파일을 읽어 리스트에 저장하는 기능 구현
Dec 4, 2022
4784324
feat(mission): 미션 목록을 리스트에 저장하는 기능 구현
Dec 4, 2022
52365e6
feat(pairmatch): 페어 정보를 관리하는 저장소 설계
Dec 4, 2022
e09784d
docs(readme): 애플리케이션과 관련한 구현 목록 작성
Dec 4, 2022
0a2beb8
feature(featurecommand): 기능의 종류를 출력하고, 그 중 하나의 입력을 받는 기능 구현
Dec 4, 2022
0443fa4
feature(outputview): 과정과 미션을 출력하는 기능 구현
Dec 4, 2022
6bbe90f
feature(pairmatchinginfo): 매칭하고자 하는 과정, 레벨, 미션을 입력 받는 기능 구현
Dec 4, 2022
758efa0
docs(readme): 크루 매칭과 관련된 기능 구현 목록 작성
Dec 4, 2022
bc26be2
feat(pairsmaker): 크루 목록을 섞은 후 매칭해 저장하는 기능 구현
Dec 4, 2022
59bc36a
feat(paircontroller): 이미 페어로 만난적이 있는 크루끼리 매칭 된다면 다시 랜덤으로 섞어서 매칭을 시도하는…
Dec 4, 2022
1de38de
feat(outputview): 페어매칭 결과를 출력하는 기능 구현
Dec 4, 2022
80b4a20
feat(findpaircontroller): 페어 결과를 조회하는 기능 구현
Dec 4, 2022
1769c1c
feat(resetpaircontroller): 페어 결과를 초기화하는 기능 구현
Dec 4, 2022
dd79965
feat(pairapplication): 애플리케이션을 종료하는 기능 구현
Dec 4, 2022
278fd48
fix(pairmaker): 테스트 코드의 모킹 방식에 부합하도록 코드 수정
Dec 4, 2022
923ce8e
feat(exceptionhandling): 예외 발생시 에러 메시지를 출력하고 값을 다시 받는 기능 구현
Dec 4, 2022
f0d92e2
refactor: 메서드를 역할에 맞게 세부분리
Dec 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
- 사전 제공 정보
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5시간 동안 모든 기능을 구현하셨나보네요!
대단합니다!! 👍

* [x] 크루 목록이 담긴 파일을 읽어 리스트에 저장한다.
* [x] 미션 목록을 리스트에 저장한다.
* [x] 페어 정보를 관리하는 저장소를 설계한다.

- 페어 매칭
* [x] 기능의 종류를 출력한다.
* [x] 기능 중 하나의 입력을 받는다.
* [x] 과정과 미션을 출력한다.
* [x] 매칭하고자 하는 과정, 레벨, 미션을 입력 받는다.

* [x] 크루 목록의 순서를 랜덤으로 섞는다.
* [x] 랜덤으로 섞인 페어 목록에서 페어 매칭을 할 때 앞에서부터 순서대로 두명씩 페어를 맺는다.
* [x] 홀수인 경우 마지막 남은 크루는 마지막 페어에 포함시킨다.
* [x] 같은 레벨에서 이미 페어로 만난적이 있는 크루끼리 다시 페어로 매칭 된다면 크루 목록의 순서를 다시 랜덤으로 섞어서 매칭을 시도한다.
* [x] 3회 시도까지 매칭이 되지 않거나 매칭을 할 수 있는 경우의 수가 없으면 에러 메시지를 출력한다.

* [x] 페어 매칭 결과를 출력한다.

- 페어 조회
* [x] 과정과 미션을 출력한다.
* [x] 조회하고자 하는 과정, 레벨, 미션을 입력 받는다.

- 페어 초기화
* [x] 커맨드를 호출하면 저장된 페어 매칭을 제거한다.
* [x] 초기화 완료 메시지를 출력한다.

- 종료
* [x] 종료 커맨드를 입력하면 애플리케이션이 종료된다.
4 changes: 4 additions & 0 deletions src/main/java/pairmatching/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package pairmatching;

import pairmatching.system.PairApplication;

public class Application {
public static void main(String[] args) {
// TODO 구현 진행
PairApplication pairApplication = new PairApplication();
pairApplication.run();
}
}
24 changes: 24 additions & 0 deletions src/main/java/pairmatching/controller/AbstractController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package pairmatching.controller;

import pairmatching.outputview.ErrorMessageOutputView;
import pairmatching.outputview.OutputView;

import java.util.Map;

public abstract class AbstractController implements Controller {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controller에서 오류가 발생할 경우 다시 해당 기능을 재요청 하기 위한 방법으로
abstract 클래스를 만들고 이를 controller 들에게 상속시킨 구조도 인상깊습니다.
다만, 객체지향적 설계의 입장에서 봤을 때는 조금 문제가 있을수도 있을 것 같습니다.
결국 각각의 단위 controller 들도 '클래스'인데 클래스를 형태를 한 메소드의 모음 같은 구조가 되버리지 않나 생각이 들어서요... 클래스 분리가 너무 '많이'된 느낌이 드는 건 사실인 것 같습니다.
이 부분 저도 더 고민이 필요할 것 같습니다. 확실히 프로그램 단위가 커지니 구조를 짜는게 쉽지 않네요...ㅎㅎ

private final OutputView outputView = new ErrorMessageOutputView();

@Override
public void process(Map<String, Object> model) {
try {
doProcess(model);
} catch (IllegalArgumentException e) {
model.put("errorMessage", e.getMessage());
outputView.print(model);

doProcess(model);
}
}

abstract void doProcess(Map<String, Object> model);
}
7 changes: 7 additions & 0 deletions src/main/java/pairmatching/controller/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pairmatching.controller;

import java.util.Map;

public interface Controller {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controller를 interface로 선언하신게 신선하네요!

void process(Map<String, Object> model);
}
24 changes: 24 additions & 0 deletions src/main/java/pairmatching/controller/FindPairController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package pairmatching.controller;

import pairmatching.outputview.OutputView;
import pairmatching.repository.PairMatchingRepository;
import pairmatching.vo.PairMatchingInfo;

import java.util.Map;

public class FindPairController extends AbstractController {
private final OutputView outputView;
private final PairMatchingRepository pairMatchingRepository;

public FindPairController(OutputView outputView, PairMatchingRepository pairMatchingRepository) {
this.outputView = outputView;
this.pairMatchingRepository = pairMatchingRepository;
}

@Override
public void doProcess(Map<String, Object> model) {
PairMatchingInfo pairMatchingInfo = (PairMatchingInfo) model.get("pairMatchingInfo");
model.put("matchedPairNames", pairMatchingRepository.findAllNamesByPairMatchingInfo(pairMatchingInfo));
outputView.print(model);
}
}
62 changes: 62 additions & 0 deletions src/main/java/pairmatching/controller/MatchingPairController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package pairmatching.controller;

import camp.nextstep.edu.missionutils.Randoms;
import pairmatching.model.Crew;
import pairmatching.model.Pair;
import pairmatching.outputview.OutputView;
import pairmatching.repository.CrewRepository;
import pairmatching.repository.PairMatchingRepository;
import pairmatching.system.util.PairsMaker;
import pairmatching.vo.PairMatchingInfo;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MatchingPairController extends AbstractController {
public static final String CREW_MATCHING_FAILED_THREE_TIMES_MESSAGE = "크루 매칭이 3회 이상 실패하였습니다.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 상수는 MatchingPairController 에서만 사용되는거 같은데 혹시 public 으로 선언하신 이유가 있을까요?!
다른 코드들도 전부 상수는 public 인거 같은데 private랑 public 이랑 어떤 차이를 두셨는지 궁금해요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트코드를에서 적절한 에러 메시지가 출력되는 지 검증하는 코드를 작성할 때 저 상수들을 사용합니다!
생각해보니 테스트 작성을 위해서 상수를 퍼블릭하게 두는 것도 바람직하지 않은 것 같네요. 이번 주 스터디 때 반영하겠습니다!

private final CrewRepository crewRepository;
private final PairMatchingRepository pairMatchingRepository;
private final PairsMaker pairsMaker;
private final OutputView outputView;

public MatchingPairController(CrewRepository crewRepository, PairMatchingRepository pairMatchingRepository, PairsMaker pairsMaker, OutputView outputView) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 컨벤션의 열제한(120)과 파라미터 갯수 제한을 초과한거 같아요!

this.crewRepository = crewRepository;
this.pairMatchingRepository = pairMatchingRepository;
this.pairsMaker = pairsMaker;
this.outputView = outputView;
}

@Override
public void doProcess(Map<String, Object> model) {
PairMatchingInfo pairMatchingInfo = (PairMatchingInfo) model.get("pairMatchingInfo");
List<Crew> crews = crewRepository.findByCourse(pairMatchingInfo.getCourse());
List<Pair> pairs;

pairs = makePairs(pairMatchingInfo, crews);
pairMatchingRepository.save(pairMatchingInfo, pairs);

model.put("matchedPairNames", pairMatchingRepository.findAllNamesByPairMatchingInfo(pairMatchingInfo));
outputView.print(model);
}

private List<Pair> makePairs(PairMatchingInfo pairMatchingInfo, List<Crew> crews) {
List<Pair> pairs;
int notMatchedCount = 0;
do {
pairs = pairsMaker.makePairs(getShuffledCrewNames(crews));
notMatchedCount++;
if (notMatchedCount == 3) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

숫자 3은 상수로 변경해주어도 좋을거 같아요!

throw new IllegalStateException(CREW_MATCHING_FAILED_THREE_TIMES_MESSAGE);
}
} while (pairMatchingRepository.hasDuplicatingAtSameLevel(pairMatchingInfo, pairs));
return pairs;
}

private List<String> getShuffledCrewNames(List<Crew> crews) {
List<String> crewNames = crews.stream()
.map(Crew::getName)
.collect(Collectors.toList());
return Randoms.shuffle(crewNames);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package pairmatching.controller;

import pairmatching.model.Course;
import pairmatching.model.Crew;
import pairmatching.repository.CrewRepository;
import pairmatching.system.convertion.NamesToCrewConverter;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ReadingCrewsFileController extends AbstractController {

public static final String BACKEND_CREWS_FILE_DIRECTORY = "src/main/resources/backend-crew.md";
public static final String FRONTEND_CREWS_FILE_DIRECTORY = "src/main/resources/frontend-crew.md";

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상대경로/절대경로와 파일 입출력에 대해서 이번에 첨 접했는데
상대경로에 대해 배우고 갑니다!

private final CrewRepository crewRepository;

public ReadingCrewsFileController(CrewRepository crewRepository) {
this.crewRepository = crewRepository;
}

@Override
public void doProcess(Map<String, Object> model) {
try {
List<Crew> backEndCrews = NamesToCrewConverter.convert(readBackEndCrews(), Course.BACKEND);
crewRepository.saveAll(backEndCrews);

List<Crew> frontEndCrews = NamesToCrewConverter.convert(readFrontEndCrews(), Course.FRONTEND);
crewRepository.saveAll(frontEndCrews);
} catch (IOException e) {
throw new IllegalStateException("크루 파일 읽기에 문제가 발생했습니다.");
}
}

private static List<String> readBackEndCrews() throws IOException {
List<String> crewNames = new ArrayList<>();
readFile(crewNames, BACKEND_CREWS_FILE_DIRECTORY);
return crewNames;
}

private static List<String> readFrontEndCrews() throws IOException {
List<String> crewNames = new ArrayList<>();
readFile(crewNames, FRONTEND_CREWS_FILE_DIRECTORY);
return crewNames;
}

private static void readFile(List<String> crewNames, String fileDirectory) throws IOException {
File file = new File(fileDirectory);
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
readLine(crewNames, bufferedReader);
bufferedReader.close();
Comment on lines +53 to +57
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파일리더를 사용하는 것도 있다는 걸 배워서 감사합니다!

}

private static void readLine(List<String> crewNames, BufferedReader bufferedReader) throws IOException {
String line = "";
while ((line = bufferedReader.readLine()) != null) {
crewNames.add(line);
}
}
}
22 changes: 22 additions & 0 deletions src/main/java/pairmatching/controller/ResetPairController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package pairmatching.controller;

import pairmatching.outputview.OutputView;
import pairmatching.repository.PairMatchingRepository;

import java.util.Map;

public class ResetPairController extends AbstractController {
private final PairMatchingRepository pairMatchingRepository;
private final OutputView outputView;

public ResetPairController(PairMatchingRepository pairMatchingRepository, OutputView outputView) {
this.pairMatchingRepository = pairMatchingRepository;
this.outputView = outputView;
}

@Override
public void doProcess(Map<String, Object> model) {
pairMatchingRepository.resetAll();
outputView.print(model);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package pairmatching.controller;

import pairmatching.model.Level;
import pairmatching.repository.MissionRepository;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class SavingMissionsController extends AbstractController {
private final MissionRepository missionRepository;

public SavingMissionsController(MissionRepository missionRepository) {
this.missionRepository = missionRepository;
}

@Override
public void doProcess(Map<String, Object> model) {
saveMissions(getLevelOneMissionNames(), Level.LEVEL1);
saveMissions(getLevelTwoMissionNames(), Level.LEVEL2);
saveMissions(getLevelFiveMissionNames(), Level.LEVEL4);
}

private static List<String> getLevelFiveMissionNames() {
return Arrays.asList("성능개선", "배포");
}

private static List<String> getLevelTwoMissionNames() {
return Arrays.asList("장바구니", "결제", "지하철노선도");
}

private static List<String> getLevelOneMissionNames() {
return Arrays.asList("자동차경주", "로또", "숫자야구게임");
}

private void saveMissions(List<String> missionNames, Level level) {
missionRepository.saveAllNamesWithLevel(missionNames, level);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pairmatching.controller;

import pairmatching.inputview.InputView;
import pairmatching.outputview.OutputView;
import pairmatching.vo.FeatureCommand;

import java.util.Map;

public class SelectingFeatureController extends AbstractController {
private final OutputView outputView;
private final InputView<FeatureCommand> inputView;

public SelectingFeatureController(OutputView outputView, InputView<FeatureCommand> inputView) {
this.outputView = outputView;
this.inputView = inputView;
}

@Override
public void doProcess(Map<String, Object> model) {
outputView.print(model);
model.put("featureCommand", inputView.getInput(model));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package pairmatching.controller;

import pairmatching.inputview.InputView;
import pairmatching.model.Course;
import pairmatching.model.Level;
import pairmatching.outputview.OutputView;
import pairmatching.repository.MissionRepository;
import pairmatching.vo.PairMatchingInfo;

import java.util.Map;

public class SelectingMissionController extends AbstractController {
private final OutputView outputView;
private final InputView<PairMatchingInfo> inputView;
private final MissionRepository missionRepository;

public SelectingMissionController(OutputView outputView, InputView<PairMatchingInfo> inputView, MissionRepository missionRepository) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분도 열제한(120)을 초과해서 inputView, 다음에 줄바꿈 해주셔야 할거 같아요!

this.outputView = outputView;
this.inputView = inputView;
this.missionRepository = missionRepository;
}

@Override
public void doProcess(Map<String, Object> model) {
printMessage(model);
readInputIntoModel(model);
}

private void readInputIntoModel(Map<String, Object> model) {
putAllMissionsToModel(model);
model.put("pairMatchingInfo", inputView.getInput(model));
}

private void printMessage(Map<String, Object> model) {
putCoursesNameToModel(model);
putMissionNamesToModel(model);
outputView.print(model);
}

private Object putAllMissionsToModel(Map<String, Object> model) {
return model.put("missions", missionRepository.findAll());
}

private static Object putCoursesNameToModel(Map<String, Object> model) {
return model.put("courses", Course.getCourseNames());
}

private void putMissionNamesToModel(Map<String, Object> model) {
model.put("level1Missions", missionRepository.findAllNamesByLevel(Level.LEVEL1));
model.put("level2Missions", missionRepository.findAllNamesByLevel(Level.LEVEL2));
model.put("level3Missions", missionRepository.findAllNamesByLevel(Level.LEVEL3));
model.put("level4Missions", missionRepository.findAllNamesByLevel(Level.LEVEL4));
model.put("level5Missions", missionRepository.findAllNamesByLevel(Level.LEVEL5));
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package pairmatching.inputview;

import camp.nextstep.edu.missionutils.Console;
import pairmatching.system.convertion.StringToFeatureCommandConverter;
import pairmatching.vo.FeatureCommand;

import java.util.Map;

public class GettingFeatureCommandInputView implements InputView<FeatureCommand> {
@Override
public FeatureCommand getInput(Map<String, Object> model) {
String input = readInput();
return StringToFeatureCommandConverter.convert(input);
}

protected static String readInput() {
return Console.readLine();
}
}
Loading