컴퓨터 프로그래밍에서 열거형(enumerated type, enumeration), 이넘(enum), 팩터(factor ← R 프로그래밍 언어와 통계학의 범주형 변수에서 부르는 명칭)는 요소, 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다. 열거자 이름들은 일반적으로 해당 언어의 상수 역할을 하는 식별자이다.
참고
https://ko.wikipedia.org/wiki/%EC%97%B4%EA%B1%B0%ED%98%95
저는 보통 Enum을 사용할 때는 고정된 상수 등을 비즈니스 로직에 선언하는 것이 아닌 클래스화 하여 사용하는 것이라 이해했습니다.
그래서 과거에는
public static final int APPLE_PRICE = 0;
public static final int APPLE_COLOR = 0;
public static final int ORANGE_PRICE = 0;
public static final int ORANGE_COLOR = 0;
위와 방식을 정수 열거 패턴(int enum pattern)이라고 하는데, 보기만해도 단점이 많아 보입니다. 먼저 타입 안전성을 보장하기가 어렵습니다. 사과와 오렌지를 비교하는 메소드를 만들면 어떻게 될까요? 동등 연산자(==)로 비교해도 아무런 경고없이 동작하게 되겠지요.
그리고 표현 방식이 참 애매합니다. 사과용 상수와 오렌지용 상수의 이름 충돌을 방지하기 위해 접두사(prefix)를 사용했습니다. 마지막으로 이를 문자열로 출력하기도 다소 까다로운 점이 있습니다.
그렇다면 열거 타입(enum type)을 사용하면 어떻게 될까요?
public enum Apple {
PRICE, COLOR
}
public enum Orange {
PRICE, COLOR
}
그렇다면 열거 타입에는 어떠한 장점들이 있을까요?
final
이라고 볼 수 있습니다.이제 예제로 장점을 살펴보겠습니다.
만약에 Y(String), 1(int), true(boolean)
3가지 타입의 값이 같은 경우 보통 소스를 어떻게 짤까요?
public String getTable1Value(int originalValue) {
if (originalValue == 1) {
return "Y";
}
else {
return "N";
}
}
public boolean getTable2Value(String originalValue) {
if (originalValue.equals("Y")) {
return true;
}
else {
return false;
}
}
위의 코드는 테이블1, 테이블2이 존재할때 Y, 1, true
의 값이 모두 같다고 사용할때 변환하는 과정입니다. 실제 업무할때 테이블 설계가 다르기 때문에 이런 경우가 있긴 하죠?
위 코드는 문제가 기능상 문제는 없지만, 몇가지 문제가 있습니다.
Y, 1, true
가 모두 같은 의미기 때문에 메소드를 매번 살펴가며 어떤 것이 같은 의미인지 확인해줘야 합니다.T, F
라는 데이터가 추가된다면 일일히 메서드에 if else
문을 추가해줘야 할 것입니다.그래서 이 부분을 Enum 클래스로 변경한다면
public enum ValueEnum {
Y(1, true),
N(0, false);
private int table1Value;
private boolean table2Value;
ValueEnum(int value, boolean flag) {
this.table1Value = value;
this.table2Value = flag;
}
public int getTable1Value() {
return table1Value;
}
public boolean isTable2Value() {
return table2Value;
}
}
Y, 1, true
, N, 0, false
가 한묶음으로 됐다는걸 확인할 수 있습니다.
public class Main {
public static void main(String args[]) {
ValueEnum valueEnumY = ValueEnum.Y;
ValueEnum valueEnumN = ValueEnum.N;
System.out.println("fruitEnumY = " + valueEnumY.getTable1Value() + " " + valueEnumY.isTable2Value());
System.out.println("fruitEnumN = " + valueEnumN.getTable1Value() + " " + valueEnumN.isTable2Value());
}
}
ValueEnum
enum 클래스를 선언해주면 바로 table1, table2가 어떤 데이터인지 확인할 수 있습니다.
예를 들어 DB에 저장된 값이 A일때, B일때 나머지 일때의 로직이 다른 경우가 있습니다.
public class Calculator {
public static long start(String str, long value) {
if (str.equals("A")) {
return value * 2;
}
else if (str.equals("B")) {
return value - 10;
}
else {
return 0;
}
}
}
원래 저였다면 static
키워드를 활용하여 사용했을 겁니다.
Table table1 = new Table("A");
String str = table1.getData();
long value = 5L;
long result = Calculator.start(str, value);
System.out.println("result = " + result);
이런 상황에서 문제가 있는게 Table에서 데이터를 가져오고 그 데이터를 또 별도의 메소드를 활용하여 진행한다는 것이였습니다.
Table
클래스와 start
메소드와의 연관관계를 설명하기 힘듭니다.따라서 Enum
클래스를 활용하여 DB에서 가져온 특정 값을 지정된 메소드와 연관관계가 있다. 즉, 특정 데이터와 메서드를 한꺼번에 실행 할 수 있게 처리하였습니다.
함수형 인터페이스를 활용하였습니다.
public enum CalculatorEnum {
CAL_A(value -> value * 3),
CAL_B(value -> value * 10);
private Function<Long, Long> expression;
CalculatorEnum(Function<Long, Long> expression) {
this.expression = expression;
}
public long calculate(long value) {
return expression.apply(value);
}
}
값이 A 또는 B일때 실행하는 메소드까지 묶었습니다.
long value2 = 13L;
CalculatorEnum calA = getCalA();
System.out.println("result2 = " + calA.calculate(value2));
CalculatorEnum calB = getCalB();
System.out.println("result3 = " + calB.calculate(value2));
위와 같이 getCalA()
라는 메소드를 통해 데이터를 바로 CalculateEnum
타입으로 변환 후 calculate
를 실행할 수 있습니다.
그리고 실제로 사용하는 곳에서도 이젠 직접 Code에게 계산을 요청하면 됩니다.
값(상태)과 메소드(행위)가 어떤 관계가 있는지에 대해 더이상 다른 곳을 찾을 필요가 없게 되었습니다.
코드내에 전부 표현되어 있고, Enum 상수에게 직접 물어보면 되기 때문입니다.
Java Enum을 활용하면
즉 상수의 값들을 좀 더 효율적으로 관리하여 가독성과 리팩토링에 도움이 된다는 걸 알 수 있습니다. 이렇게 정리한 내용을 바탕으로 Enum을 적극적으로 활용하도록 고민해야겠다.
이 글은 학습을 위해
https://techblog.woowahan.com/2527/
https://madplay.github.io/post/use-enums-instead-of-int-constants#google_vignette
읽고 정리한 글입니다.