Home 상수를 관리하는데 Enum을 사용해야 할까?
Post
Cancel

상수를 관리하는데 Enum을 사용해야 할까?

나는 그간의 미션에서 상수를 관리할 때 하나의 클래스에 public static final을 사용해 관리하고 있었다.

1
2
3
4
5
6
7
8
9
10
11
public class GameConstants {  
  
	public static final int RANDOM_NUMBER_MIN_RANGE = 0;  
	public static final int RANDOM_NUMBER_MAX_RANGE = 9;  
	public static final int MOVE_DISTANCE = 1;  
	public static final int CAN_MOVE_MIN_VALUE = 4;  
	public static final int CAR_SIZE_MIN = 2;  
	public static final int CAR_NAME_MAX_LEN = 5;

	private GameConstants() {}  
}

Enum 포스팅에서 Enum을 학습하면서 Enum의 장점도 알았다.

하지만 위 코드를 Enum으로 관리했을 때 사용하는 부분에 맘에 들지 않는 부분이 생긴다.

1
2
3
4
5
public void move() {  
	if (canMove()) {  
		this.moveDistance += MOVE_DISTANCE;  
	}  
}

정의한 상수를 사용하는 부분에서 static-import를 사용하면 위와 같이 사용할 수 있다. 가독성 측면에서 좋다고 생각한다.

하지만 Enum으로 관리한다면?

1
2
3
4
5
public void move() {  
	if (canMove()) {  
		this.moveDistance += MOVE_DISTANCE.getValue();  
	}  
}

get 함수를 호출해야 해당 int 값을 가져올 수 있다.

이러한 부분에서 Enum을 사용하면 단순하게 가독성만 저해하는 것 아닌가? 라는 생각이 들게 된 것이다.

그래서 왜 Enum을 사용해야 돼?

그냥 가독성만 안좋아지는 것 같은데 Enum을 사용하는 것이 왜 좋은 지 알아보기 위해 static final의 문제점을 Enum과 비교하여 알아보자.

  • 유형 안정성이 부족하다.

아래 코드를 보자.

1
2
public static final int STATUS_ON = 1;
public static final int STATUS_OFF = 2;

전혀 문제가 없어보인다. 하지만 Enum과 비교하면?

1
2
3
4
5
6
7
8
public enum STATUS {
	ON(1),
	OFF(2);

	private final int status;

	/...
}

ON, OFF에 해당하는 값은 모두 int 유형의 값으로 제한된다. 만약 static final 키워드를 통해 관리한다면 String 타입이 들어갈 수도 있고, 이 외 다른 모든 타입의 변수를 할당할 수 있는데 이런 경우 추적에 어려움을 겪을 수 있다.

  • 값의 반복이 불가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public enum LottoResultStatus {  
  
	FAIL(0, 0, (matchCount, withBonusNumber) -> matchCount < 3),  
	THREE_MATCH(3, 5_000, (matchCount, withBonusNumber) -> matchCount == 3),  
	FOUR_MATCH(4, 50_000, (matchCount, withBonusNumber) -> matchCount == 4),  
	FIVE_MATCH(5, 1_500_000, (matchCount, withBonusNumber) -> matchCount == 5 && !withBonusNumber),  
	FIVE_MATCH_WITH_BONUS(5, 30_000_000, (matchCount, withBonusNumber) -> matchCount == 5 && withBonusNumber),  
	ALL_MATCH(6, 2_000_000_000, (matchCount, withBonusNumber) -> matchCount == 6);  
	  
	private final long matchCount;  
	private final int prize;  
	private final BiPredicate<Long, Boolean> checker;  
	  
	LottoResultStatus(long matchCount, int prize, BiPredicate<Long, Boolean> checker) {  
		this.matchCount = matchCount;  
		this.prize = prize;  
		this.checker = checker;  
	}  
	  
	public static LottoResultStatus checkResult(long matchCount, boolean withBonusNum) {  
		return Arrays.stream(LottoResultStatus.values())  
				.filter(status -> status.checker.test(matchCount, withBonusNum))  
				.findAny()  
				.orElse(FAIL);  
	}

	/...
}

내가 짰던 로또 결과 Enum 클래스의 일부이다. Enum에 조건식을 삽입해 Enum의 요소를 반복하며 조건에 부합하는 요소가 있는 지 확인하는 메서드를 가지고 있다.

꼭 이러한 조건 검증과 같은 구현이 아니어도, 반복이 필요한 부분은 얼마든지 있다. static final을 사용하면 반복을 위해 더 많은 노력이 필요하다.

  • 확장성이 제한되어 있다.

위의 코드에서도 볼 수 있다시피 상수와 관련된 추가 동작을 Enum에서는 쉽게 구현해낼 수 있다. 하지만 static final로 관리했을 때는 불가능하다.

  • Enum을 사용했을 때 의미 파악이 훨씬 용이하다.

확실하게 관련된 값들을 관리하게 되어 enum 클래스 네이밍과 함께 의미를 파악하기 훨씬 수월해진다.

  • Enum은 값을 제한시킬 수 있다.
1
2
3
4
5
6
7
8
9
public String getResult(String input) {
	if (input.equals(InputMessage.HELLO)) {
		return "HELLO";
	}

	if (input.equals(InputMessage.HI)) {
		return "HI";
	}
}

static final을 사용했을 때는 어떠한 String 값이 들어가도 제한할 수 없게 된다. 하지만 Enum을 사용한다면?

1
2
3
4
5
6
7
8
9
public String getResult(InputMessage input) {
	if (input == InputMessage.HELLO) {
		return "HELLO";
	}

	if (input == InputMessage.HI) {
		return "HI";
	}
}

결론

저번에 포스팅한 Enum의 장점에 더해 static final에 비교한 Enum의 장점까지 알아보았다.

나는 대부분의 상황에서 아마 Enum을 사용하게 될 것 같다.

하지만 단순하게 상수를 정의해야 하는 상황이 온다면 가독성에서 조금이나마 이점이 있는 static final 키워드를 사용해 관리하지 않을까 싶다.

This post is licensed under CC BY 4.0 by the author.