타입 안전 열거 패턴은 열거한 값들을 그대로 가져온 다음에는 확장이 불가능한 반면, 열거 타입은 추가적인 값의 확장이 가능합니다. 그러나 열거 타입을 확장하는 것은 일반적으로 권장되지 않습니다.
그 이유는 다음과 같습니다:
특정 타입이 다른 타입을 확장(상속)하게 되면, 확장된 타입의 인스턴스는 기반 타입의 인스턴스로도 취급될 수 있지만, 그 반대는 성립하지 않는다는 뜻입니다.
기반 타입과 확장된 타입들의 원소를 모두 순회할 수 있는 방법이 마땅치 않습니다.
확장성을 높이기 위해서는 여러 가지 고려해야 할 부분이 있습니다.
그러나 연산 코드(operation code 또는 opcode)와 같이 확장 가능한 열거 타입이 적절한 상황도 있습니다. 이 경우, 기본 아이디어는 열거 타입이 임의의 인터페이스를 구현할 수 있다는 점을 이용하는 것입니다.
public interface Operation {
double apply(double x, double y);
}
public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
}
열거 타입인 BasicOperation은 확장이 불가능하지만, 인터페이스인 Operation은 확장이 가능합니다. 따라서 Operation을 구현한 다른 열거 타입을 정의하여 기본 타입을 사용할 수 있습니다.
이러한 방식으로, Operation 인터페이스를 구현하는 새로운 열거 타입을 정의할 수 있습니다. 이렇게 하면 Operation 인터페이스의 메서드를 구현함으로써 새로운 동작을 추가하고 확장할 수 있으며, 코드의 유연성과 확장성을 높일 수 있습니다.
public enum ExtendedOperation implements Operation {
EXP("^") {
public double apply(double x, double y) {
return Math.pow(x, y);
}
},
REMAINDER("%") {
public double apply(double x, double y) {
return x % y;
}
};
private final String symbol;
ExtendedOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
}
따라서, 클라이언트는 인터페이스를 구현해 자신만의 열거 타입을 만들 수 있으며, 기본 열거 타입의 인스턴스가 쓰이는 모든 곳에서 새로 확장한 열거 타입의 인스턴스로 대체해 사용할 수 있습니다.
참고 레퍼런스