본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] int 상수 대신 enum 을 사용하자.

by 돼지왕 왕돼지 2016. 12. 19.
반응형

 [Effective Java] int 상수 대신 enum 을 사용하자.


abstract enum, Comparable, constant specific method implementation, constant-specific method implementation, Effective JAVA, enum, enum switch, enum 상수, enum 인스턴스, enum.valueof, Final, fromstring, iNT, int enum, int enum pattern, int 상수, loop, object, print, Private, Public, public static final, public 접근자 메소드, Serializable, static 필드, strategy enum type, string enum, Switch, switch 문, toString, valueOf, [Effective Java] int 상수 대신 enum 을 사용하자., 가독성, 강력, 공통 동작, 그룹 사이즈, 능사, 단일 요소, 단점, 데이터, 로딩, 맴버, 메모리, 메소드, 문자열, 문자열 변환, 문자열 비교, 보장, 분기, 불변, 비교 가능, 상속, 상수, 상수 필드, 생성, 생성자, 성능, 시간, 싱글톤, 안전, 열거형 상수, 오버라이딩, 오작동, 오타, 유지보수, 인스턴스, 인스턴스 생성, 인터페이스, 장점, 전략 enum 타입, 전략 enum 패턴, 초기화, 최상위 수준 클래스, 출력 가능한 문자열, 취약, 컴파일, 컴파일 시점, 컴파일 시점 타입 안전, 코드 공유, 클래스, 타입 안전, 토스터, 특정 상수 메소드, 특정 상수 메소드 구현, 편리, 필드, 하드 코딩, 항상, 휴대폰

-
int enum pattern 이라고 불리는 int 를 이용한 분기는 단점이 많다.


-
타입 안전을 보장하지도 않고, 편리하게 사용할 수 있는 방법도 제공하지 않는다. 즉 취약하다.


-
int enum 상수를 출력 가능한 문자열로 쉽게 바꾸는 방법도 없다.


-
하나의 int enum 그룹에 있는 모든 상수를 loop 처리하거나, 그룹 사이즈를 알 수 있는 좋은 방법도 없다.


-
int enum 패턴의 변이로 String enum 패턴이 있는데 더더욱 바람직하지 않다.
print 하는 부분에서는 좋지만, 문자열 비교에 의존하여 성능 문제가 생길 수도 있다.
더욱 문제가 되는 것은 필드명 대신 string 상수 값을 클라이언트 코드에 하드 코딩하면 오타 -> 오작동 되기 쉽다.


-
자바의 enum 타입은 어엿한 클래스이다.
단순 int 값으로 된 다른 언어의 것보다 훨씬 강력하다.
enum 타입은 클래스로써, public static final 필드를 사용해 열거형 상수마다 하나의 인스턴스를 외부에 제공한다.
enum 의 상수는 final 이다.
enum 인스턴스는 생성할 수 없고, 상속을 받을 수도 없기에, 인스턴스는 없지만 선언된 enum 상수는 존재한다.
즉, enum 타입은 인스턴스 생성을 제어하며, 싱글톤을 일반화시킨 것으로 본질적으로는 단일 요소 enum 이다.


-
enum 은 컴파일 시점의 타입 안전을 제공한다.


-
enum 의 toString() 을 호출하면 enum 을 출력 가능한 문자열로 변환해준다.


-
enum 은 자기 나름의 메소드와 필드를 추가할 수 있고, 인터페이스를 구현할 수도 있다.
Object 클래스의 모든 메소드를 양질로 구현한 메소드를 제공한다.
Comparable 과 Serializable 인터페이스를 구현하고 있다.


-
데이터를 enum 상수와 연관시키려면, 인스턴스 필드를 선언하고 그 데이터를 인자로 받아 필드에 넣는 생성자를 작성하면 된다.
enum 은 본래 불변이므로 모든 필드는 final 이어야 한다.
필드가 public 이 될 수 있지만, private 으로 만들고 public 접근자 메소드를 제공하는 것이 더 좋다.


-
만일 enum 이 널리 사용된다면 최상위 수준의 클래스로 해야 한다.


-
enum 을 통해 동작분기를 할 때 switch 문을 생각하기 쉽지만, 유지보수 측면에서 특정 상수 메소드 구현 ( constant-specific method implementation ) 방법이 좋다.

// 그다지 좋은 방법은 아님.

public enum Operation{
PLUS, MINUS, TIMES, DIVIDE;

double apply( double x, double y ){
switch( this ){
case PLUS:
return x + y;
...
}
}
}

// 유지보수 측면에서 좋은 방법.
public enum Operation{
PLUS{ double apply( double.x, double y ){ return x + y; } },
...

abstract double apply( double x, double y );
}


enum constant 가 늘어나거나 줄어들 때 유지보수가 더 쉽다.
단, enum 상수간의 코드 공유를 어렵게 만들기 때문에 "항상" 능사는 아니다.


-
enum 타입에서 toString 메소드를 오버라이딩 할 때는, fromString 메소드의 작성을 고려하자.

왜냐하면 enum.valueOf(String) 이 enum 값으로 치환해주는데, toString 과는 내용이 다를 수 있기 때문이다.

private static final Map<String, Operation> stringToEnum = new HashMap<String, Operation>();


public static Operation fromString( String symbol ){

return stringToEnum.get( symbol );

}



-
enum 생성자들은 컴파일 시점의 상수 필드는 사용 가능하지만, 해당 enum 의 static 필드는 사용할 수 없다.
생성자가 실행되는 시점에 그런 static 필드들이 아직 초기화되지 않은 상태이기 때문이다.


-
코드 공유를 해야 하면서 유지보수를 위해 constant-specific method implementation 를 사용하고 싶다면,
전략(strategy) enum 타입을 하나 더 정의하는 방법으로 개선될 수 있다.

enum PayrollDay{

Monday( PayType.WEEKDAY ), ... , SUNDY( PayType.WEEKEND );


private final PayType payType;

PayrollDay( PayType payType ) { 

this.payType = payType; 

}


double pay(double hoursWorked, double payRate ){

return payType.pay( hoursWorked, payRate );

}


private enum PayType{

WEEKDAY{

double overTimePay( double hours, double payRate ){

return ...;

}

},


WEEKEND{

double overTimePay( double hours, double payRate ){

return ...;

}

}


abstract double overTimePay( double hours, double payRate );


double pay( double hourWorked, double payRate ){

double basePay = hourWorked * payRate;

return basePay + overTimePay( hourWorked, payRate );

}

}

}



-
enum constant 는 switch 문 비교 가능하기 때문에 편리하다.


-
일반적으로 enum 의 성능은 int 상수와 필적할만하다.
int 상수에 비해 enum 의 성능 상 단점은 enum 타입을 로딩하는데 소요되는 메모리와 시간이다.
휴대폰이나 토스터 같이 자원이 제약된 장치들이 아니면, 실제로 큰 문제가 되지 않는다.



Summary


int 상수에 비해 enum 타입은 장점이 많다.
가독성이 좋고, 안전하며, 강력하다.
많은 수의 enum에서 명시적인 생성자나 멤버가 필요없지만, 또 다른 enum 은 데이터를 각 상수와 연관시키고 그 데이터에 영향을 받는 메소드를 제공하여 이점을 누릴 수 있다.
하나의 메소드에서 여러 상수와 관련되는 처리를 복합적으로 하는 enum 은 거의 없다.
드물지만 필요한 경우에는 switch 문 대신 enum 대신 특정 상수 메소드를 사용하자.
그리고 만일 여러 개의 enum 상수가 공통 동작을 공유하면, 전략 enum 패턴의 사용을 고려하자.





반응형

댓글