기존에 나는 Constant Interface를 사용해서 메시지들을 관리했었다.
코드 리뷰를 통해서 Enum Class를 추천해주셔서 Constant Interface랑 무엇이 다를까 찾아보니 문제점들이 있었다.
Constant Interface의 문제점
1. Implements 할 경우 사용하지 않을 수도 있는 상수를 포함하여 모두 가져오기 때문에 계속 가지고 있어야 한다.
2. 컴파일할 때 사용되겠지만, 런타임에는 사용할 용도가 없다. (Marker Interface는 런타임에 사용할 목적이 있으므로 다름)
3. Binary Code Compatibility (이진 호환성)을 필요로 하는 프로그램일 경우, 새로운 라이브러리를 연결하더라도, 상수 인터페이스는 프로그램이 종료되기 전까지 이진 호환성을 보장하기 위해 계속 유지되어야 한다.
4. IDE가 없으면, 상수 인터페이스를 Implements 한 클래스에서는 상수를 사용할 때 네임스페이스를 사용하지 않으므로, 해당 상수의 출처를 쉽게 알 수 없다. 또한 상수 인터페이스를 구현한 클래스의 하위 클래스들의 네임스페이스도 인터페이스의 상수들로 오염된다.
5. 인터페이스를 구현해 클래스를 만든다는 것은, 해당 클래스의 객체로 어떤 일을 할 수 있는지 클라이언트에게 알리는 행위이다. 따라서 상수 인터페이스를 구현한다는 사실은 클라이언트에게는 중요한 정보가 아니다. 다만, 클라이언트들을 혼동시킬 뿐이다.
6. 상수 인터페이스를 Implements 한 클래스에 같은 상수를 가질 경우, 클래스에 정의한 상수가 사용되므로 사용자가 의도한 흐름으로 프로그램이 돌아가지 않을 수 있다.
-참조
6번에서 설명하는 내용
이런 문제들을 이해하고 Enum클래스를 사용하기로 했다.
- 에러가 발생할 때마다 Stirng은 불변 객체이기 때문에 + 연산을 항상 해야되는 문제가 있어서 PREFIX를 제거하는게 좋을지 아니면 상수 메시지를 입력할 때 "[ERROR] "를 다 적어줄지 고민된다🥲 나중에 Prefix를 변경해줘야 될 때를 생각하면 지금은 이 구조가 좋다고 생각한다.
이대로 상수처리만 하기는 아쉬워서 Enum을 조금더 공부해보기로 했다.
코드 리뷰를 처음 받아보는데 정말 너무 감사한분들이 많다..ㅜㅜ
- ㅜㅜ 포비의 유튜브를 보면서 if - else로 결합되어있을 때는 메서드로 분리하기 힘들었는데 else예약어를 사용하지 않으니까 메서드 분리하기가 더 쉬워졌다.
리뷰를 받고 else 예약어를 효율적으로 작성하는 법을 찾다보니 Enum으로도 활용할 수 있다는 것을 알았다.
else 문이 끝나고 추가로 score에 대한 로직이 존재한다면 코드를 끝까지 내려가서 확인해야된다.
else if, else 예약어들을 제거하고 if 문 내부에서 조기 반환(early return)을 해주면 문제를 해결할 수 있다.
Enum을 활용하자!
학생은 name과 score를 가지고 있다. 모든 학생들을 담고 있는 상수를 정의해보자!
학생들의 이름과 점수는 Student라는 열거형 객체를 통해 관리되며 fromName() 메서드를 통해 이름에 해당하는 열거형 객체를 반환받을 수 있다.
이전의 코드에 비해서 매우 깔끔해진 코드를 볼 수 있다.
- 무수히 많은 분기문들을 제거
- getStudentScore() 메서드는 한 줄로 간단하게 구현
분기문을 줄이려는 노력을 하다 보면 메서드를 더 많이 분리하게 된다. 이는 하나의 메서드가 하나의 기능만 하도록 도와주는데, 그러다 보면 코드는 점차 객차지향적으로 변하게 될 것이다.
위 Enum을 활용하는 모든 예제는 아래 링크를 통해서 학습했습니다.
참조 - else 예약어를 쓰지 않는다.
이번 자동차 경주 과제에서 Enum을 활용하면서 고민한 부분
기능 요구사항을 보면 "전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다." 내용이 있었다.
- Random으로 들어온 값이 4이상의 값이면 한칸 움직이고, 미만이라면 멈춰있다는 것을 어떻게 표현할 수 있을까 고민했었다.
이동욱님의 글을 보면서 수정할 수 있었다. - 2. 상태와 행위를 한곳에서 관리
Enum을 활용해서 움직인다면 한칸 움직이고, 멈춰있다면 그대로라는 것을 표현할 수 있었다!!
나는 Car객체를 이전 포스팅에 올린것처럼 원시 타입을 포장했었다.
- 이름은 5자 이하만 가능하다는 요구사항
primitive type인 moveCount 변수를 컴파일러가 기본값 0을 넣어줄텐데 초기화를 시켜줘야 될까 고민이 있었지만 그래도 적어주는게 처음 제 코드를 보시는 분들에게도 편할거 같아서 초기화 시켜줬습니다!
움직임에 관련된것은 MoveCount 객체에서 책임지도록 하고 싶었는데 고민이 생기기 시작했다.
- 자동차라는 객체가 움직이거나 멈춘다는게 맞는거 같아서 MoveCount라는 객체가 없어도 된다는 생각
- MoveCount 객체에서 움직임을 관리하고 Car 객체에서 움직이거나 멈추는 것을 MoveCount객체에게 위임하자는 생각
이번 주에 MVC패턴을 다시 공부하면서 Controller에서 View와 Model 객체들을 이용해서 사용자에게 데이터를 전달하는 것처럼
Car객체에서 Name, MoveCount 객체를 이용해도 괜찮지 않을까 라는 생각을 했지만
MOVE_VALUE라는 상수는 값을 체크해서 움직일지, 정지할지 정하는 값이기 때문에 Enum에 넣어주는게 맞는거 같아서 MoveCount를 제거하고 Enum에서 4이상인지 미만인지를 체크하고 계산해주는 로직까지 같이 넣어주기로 결정했다.
- 많은 의견 부탁드립니다🥹🥹
덕분에 자동차 객체에서는 Enum을 통해서 데이터를 처리할 수 있게 되었다!!
저번 주 코드 리뷰 덕분에 많은 키워드들을 얻을 수 있었고 피드백 주신 내용들을 이번 주 과제에 적용하려고 노력하면서 많이 배운거 같습니다. 이번 주도 시간 되시면 부탁드려요!!
피드백 주신분들 다시한번 감사드립니다🤗🤗🤗🤗🤗
'개발' 카테고리의 다른 글
EnumMap 적용하기! (1) | 2023.11.08 |
---|---|
매직넘버, 리터럴 어디까지 상수 처리해야 돼? (4) | 2023.11.06 |
원시 타입을 포장하자! (0) | 2023.10.31 |
Junit으로 Scanner, System.out.println() 테스트하기 (0) | 2023.10.25 |
Spring에서 Redis Geofencing기능 활용하기 (0) | 2023.10.06 |