타입 안전 열거 패턴은 확장할 수 있으나 열거 타입은 그럴수 없다.

 

그러나 사실 대부분 상황에서 열거타입을 확장하는건 좋지 않은 생각이다.

 

그런데 확장할 수 있는 열거 타입이 어울리는 쓰임이 최소한 하나는 있다.

 

바로 연산코드(operation code 혹은 opcode)다.

 

이따금 API가 제공하는 기본 연산 외에 사용자 확장 연산을 추가할 수 있도록 열어줘야 할 때가 있다.

 

이 상황을 열거타입을 사용해 인터페이스 처럼 적용해보자

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은 확장할 수 있고,

이 인터페이스를 연산의 타입으로 사용하면 된다.

 

인터페이스를 활용해 지수 연산(EXP)과 나머지 연산(%)을 추가해보자.

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;
    }
}

apply를 Operation에 선언되어 있으니 열거타입에 따로 추상 메소드로 선언하지 않아도 된다.

 

한정적 와일드카드를 이용해 활용하는 방법을 봐보자

private static void test(Collection<? extends Operation> opSet,
                         double x, double y) {
  for (Operation op : opSet)
    System.out.printf("%f %s %f = %f%n",
                      x, op, y, op.apply(x, y));
}

인터페이스를 이용해 확장 가능한 열거 타입을 흉내 내는 방식에도 한가지 사소한 문제가 있다.

 

바로 열거 타입끼리 구현을 상속할 수 없다는 점이다.

 

Operation 예시처럼 BasicOperation과 ExtendedOperation 모두에 연산기호를 저장하고 찾는 로직이 들어가야 한다.

+ Recent posts