각 주차의 미션들을 리뷰하면서 어떤 것을 배웠고 부족했던점들은 무엇이 있는지에 대해 정리합니다.
당시 제출 하였던 소스코드는 Github에서 확인하실 수 있습니다.
당시 최초 미션 저장소에 남겨져있던 커밋 기록이 힌트가 되어 커밋 메시지를 어떻게 남겨야 하는지에 대해 알 수 있었다.
refactor: commit message라는 키워드로 구글링 하여 유다시티 커밋 메시지 스타일 가이드를 알게 되었고 지금까지도 계속 사용하는 중이다.
Java의 정석을 구매하여 기초적인 자바 문법과 자바의 동작 구조에 대해 학습하였고,
여러 자바 프로젝트들의 코드를 보면서 자주 쓰이는 API가 무엇인지 파악하며 학습하였다.
또한, 프로그램이 동작하기 위해 어떤 구조를 갖춰야 하는지 미약하게 나마 알 수 있었다.
당시 객체 지향에 대해 공부하면서, 객체 지향 5대 원칙중 단일 책임 원칙에 대해 흥미를 크게 느껴 이와 관련된 고민들을 많이 했었다.
'이 메소드를 이 클래스에서 하는게 맞을까?', '어디까지를 하나의 객체로 봐야하지?', '내가 객체와 클래스에 대해선 정확히 이해를 하고 있는건가?'
처음 연습삼아 구현을 할땐 프로그램 실행 결과를 보면서 프로그램 실행 순서에 맞게 구현했었다.
하지만 어떤 클래스에서 어떤 역할을 해야하는지에 대한 감각이 많이 부족하여 하나의 기능을 여러 클래스가 갖게 되고, '도대체 누가 책임을 져야하는 거지?'
만 끊임 없이 고민하게 되었다.
이후, 미션이 시작되고 기능 단위 구현을 하면서 main에서 시작하는 것이 아닌, 기능에서 시작하여 클래스들을 만들고 이후 main에서 호출하여 동작시키는 방식으로 구현을 하니 이전에 헷갈렸던 부분들을 많이 해소 시킬수 있었고, 신기했다.
기능 단위 구현이 주는 이점이 무엇인지에 대해 알 수 있었다.
'숫자야구 게임에서의 '한 판'은 뭘까?'
에 대해서 고민을 많이 했다. 게임의 최소 단위가 무엇인가 에 대해 집착을 많이 했던 것 같다.
'숫자 야구가 시작되고 컴퓨터의 번호를 맞출때 까지가 한 판이라고 할 수 있나?'
'사용자가 숫자를 입력할 때, 사용자의 입력 번호와 컴퓨터의 번호를 담고 있는것이 한 판이고 그 판에 대한 결과가 힌트로 주어진다고 봐야하나?'
나는 후자를 택했다.
하지만 어떤 설계가 더 좋은 설계인지를 떠나 내가 더 중요시 했어야 하는건 이런 생각과 의도를 잘 전달시켰어야 한다는 것이다.
누구나 쉽게 공감할 수 있는 코드가 좋은 코드다.
private static void startBaseballGame(ArrayList<Integer> targetNumberList) {
ArrayList<Integer> userNumberList = setUserNumberList();
BaseballGame baseballGame = new BaseballGame(userNumberList, targetNumberList);
if(!baseballGame.getIsWinning()) {
printResult(baseballGame);
startBaseballGame(targetNumberList);
return;
}
OutputView.printWinningMessage();
}
baseballGame은 너무나도 추상적인 이름이다.
내가 한 판이 뭔지에 대해 고민했고 내가 정의한 한 판에 대해 표현하려고 했다면,
Phase나 Round같은 이름을 활용하여 표현했어야 한다.
불용어는 중복이다. 변수 이름에 variable이라는 단어는 단연코 금물이다. 표 이름에 table이라는 단어도 마찬가지다. NameString이 Name보다 뭐가 나은가?
Clean Code - Robert C. Martin
public class TargetNumberListGenerator {
private static final int MAX_NUM = 9;
private static final int MIN_NUM = 1;
private static final int NUM_LEN = 3;
private final ArrayList<Integer> targetNumberList;
public TargetNumberListGenerator() {
targetNumberList = setTargetNumberList();
}
public ArrayList<Integer> getTargetNumberList() {
return targetNumberList;
}
private ArrayList<Integer> setTargetNumberList() {
String targetNumberString;
targetNumberString = generateRandomNumberString();
return StringHandler.addToList(targetNumberString);
}
private String generateRandomNumberString() {
String randomNumberString = "";
int randomNumber = generateRandomNumber();
randomNumberString += Integer.toString(randomNumber);
while(randomNumberString.length() < NUM_LEN) {
randomNumber = generateRandomNumber();
if (randomNumberString.contains(Integer.toString(randomNumber))) {
continue;
}
randomNumberString += Integer.toString(randomNumber);
}
return randomNumberString;
}
}
미션을 제출한 후, Clean Code를 읽기 시작하였고 읽은지 하루만에 내가 어떤 잘못들을 하고 있었는지 깨달을 수 있었다.
당장 하나의 클래스만 봐도 List, String, NUM, LEN 등, 좋지 않은 변수명들을 쉽게 발견 할 수 있었다.
public static String getUserString() {
Scanner scanner = new Scanner(System.in);
System.out.println(INPUT_MESSAGE);
return scanner.nextLine();
}
public static int getNewGameOrQuitNumber() {
Scanner scanner = new Scanner(System.in);
System.out.println(NEW_GAME_OR_QUIT_MESSAGE);
return scanner.nextInt();
}
scanner의 사용에서 발생하는 흔한 문제를 마주 쳤을때, 왜 문제가 발생 했는지에 대해 공부하는게 아닌 단순히 문제를 해결시키는데 급급하였다.
당시 미션 제출까지 시간이 부족 했던것도 아니였다.
단순히 빨리 완성시키고 싶다, 빨리 문제를 해결하고 싶다는 마음에 적게 된 코드라고 생각한다.
부끄러운 마음가짐이다.