1. 과제 정의
2. 구현 과정
이번 미션의 주요 목표는 함수 분리와 테스트 도구 활용이다. 따라서 JUnit5 사용법을 학습했고, 구현부에서는 역할과 책임을 기준으로 기능을 최대한 분리하려고 노력했다. 다음은 구현하면서 고민했던 사항들이다.
- 클래스 분리: 기능 요구 사항에 따라 클래스를 정의하고 역할에 따라 분리하여 각 클래스가 담당하는 기능을 명확히 하기.
- Car 클래스 도메인 분리: 이름과 전진 횟수를 상태로 가지는 Car를 객체로서, domain으로 따로 빼기.
- 회차별 게임 진행상황 관리: 게임 진행 상태를 기록하는 record를 활용하여 GameService가 출력 기능을 분리하도록 구현
- GameService 클래스: 게임 실행 로직과 유틸리티 기능을 포함하여 게임 전반을 관리하는 역할을 담당.
3. 피드백 정리
3.1. 개인 피드백 / 앞으로 개선해 나가야 할 것
1. 생성자 주입 방식도 고민
필드에서 직접 객체를 생성해서 의존성을 설정하면 결합도가 높아진다. 따라서 생성자를 통한 외부 주입 방식도 고려해볼만 하다.
2. 문자열도 enum처리 고려
반복되거나 의미가 명확한 문자열은 enum으로 처리하여 코드 가독성을 높인다.
3. 한 줄의 코드라도 의미가 전달되게
코드는 한 줄이라도 의미가 명확해야 하며, 다른 사람이 보았을 때 의도를 쉽게 이해할 수 있도록 네이밍과 코드를 간결하게 작성해야 한다.
4. StringBuilder 사용
StringBuilder는 문자열을 수정할 때 매번 새로운 객체를 생성하지 않고 기존 버퍼를 이용해 성능을 개선하므로, 반복적인 문자열 결합에서 메모리 효율성과 속도 면에서 유리하다.
5. 정규표현식 쓸 때 Pattern.complie()도 고려
String regex = "^[a-zA-Z0-9가-힣]+$";
String input = "문자열"
input.matches(regex));
위와 같이 matches() 메소드는 Pattern 객체를 생성하고 다 쓰면 메모리에서 회수한다. 즉, matches()를 N번 사용한다면 그때 마다 새로운 객체를 계속 생성한다. 만약에 같은 정규표현식을 여러번 사용할 거면 매번 객체를 새로 생성하는 것이기 때문에 비효율적이다.
Pattern pattern = Pattern.compile(regex);
pattern.matcher(input).matches()
따라서 다음과 같이 Pattern 객체를 만들고 해당 객체를 재활용하는 방식으로 하면 성능을 개선할 수 있다. 복잡한 코드 방식도 아니므로 웬만하면 Pattern 객체를 초기화해서 재활용하는 방식으로 해야겠다. (문자열의 길이나 반복횟수가 작으면 큰 차이가 없지만 반복횟수 1000만번, 문자열 길이 1000개 정도의 케이스에서는 의미있게 차이가 난다.)
6. 자료구조형 getter 사용할 때 복사해서 리턴
public List<Car> getCars() {
return cars;
}
위와 같이 배열을 그냥 리턴하면 참조 반환(reference return)이 되어서 외부에서 cars를 수정하면 인스턴스에도 영향을 준다. 따라서 배열을 리턴할 때는 unmodifiableList(), List.of(), List.copyOf(). new ArrayList<>()로 복사해서 반환
3.2. 공통 피드백
- 변수명에 자료형을 포함하지 않고 의미 있는 이름을 사용
- 메서드는 하나의 기능만 수행하도록 작성하여 코드의 역할을 명확히 하기
- 구현 순서도 코딩 컨벤션으로
- 작은 단위부터 큰 단위로 상향식으로 테스트방법 고려
- 값 하드코딩 금지
4. 그 외 기타 배운 점 정리
4.1. static 메소드의 기준
객체의 상태와 무관하며, 특정 클래스가 보편적으로 수행해야 할 기능일 때 static 메소드로 작성한다.
4.2. private 메서드 테스트 방법
private 메소드를 테스트할 상황이라면 private 메서드는 해당 위치에서 적절치 않으며, 책임을 명확히 분리하고 public 메서드로 구성된 별도 클래스로 추출하는 것이 바람직 할 수도 있다. => 디스코드에 해당 내용에 대해서 여러 의견이 있는 것 같아서 유연하게 생각해봐야 할듯
4.3. 상수 클래스와 에러 메시지 관리
에러 메시지와 같은 상수는 별도의 상수 클래스로 분리하여 관리하면 유지 보수성이 향상된다. 혹은 enum을 사용할 수도
4.4. List.of, Arrays.asList, new ArrayList<>() 차이
위 링크에 가면 잘 설명 하고 있다.
정리하면 List.of는 변경 불가능한 불변 리스트를 다룰 때 효율적이다.(쓰레드 안정성이나 메모리 사용) 따라서 절대 변경이 되지 말아야 할 상황에서는 List.of를 사용하는 것이 좋다.
List.of와 Arrays.asList의 차이점은 다음과 같다.
Arrays.asList는 원소 추가/삭제는 안되지만 수정은 가능하다. 이미 만들어진 원소 배열에 List.of는 깊은 복사를 수행하고 null값을 허용안한다. Arrays.asList는 얕은 복사를 수행하고 null값을 허용한다.
또한 collections.unmodifiableList의 기능은 리스트를 변경 불가능하게 만드는 기능이다.
다만, 참조하는 형식(얕은 복사)이므로 Arrays.asList와 비슷하다.
4.5. record class
자바에서는 record클래스라는 것이 있다. record 클래스는 간결하게 불변 데이터 객체를 정의하기 위해 사용되며, 필드를 자동으로 선언하고 생성자, equals, hashCode, toString 메소드를 자동 생성해준다. 결론은 record는 예약어이다.
따라서 record는 클래스나 변수명으로 쓰는 것을 지양해야 겠다.