인생은 여행

자바 functional enum 본문

자바

자바 functional enum

산떠 버하둘 2020. 11. 18. 22:10

자바의 열거형 자료구조인 enum 은 Java5 부터 지원되었다. enum의 장점이 여러 가지 있겠지만 가장 큰 장점은 런타임에 해야할 일들 중 상당 부분을 컴파일러에게 대신 시킬 수 있다는 것 아닐까 한다. 무슨 말인고 하니, 어떤 상수 세트를 int 형이나 String 형으로 선언했다고 하면, 이 상수 값을 주고 받을 때 validation도 해야할 것이고 장황하게 비교 구절이 들어갈 것이며 필요하다면 값 변환도 할 것이다. 물론 enum을 사용한다고 해서 비교 로직이 필요 없는 것은 아니지만 코드로 구현해야할 부분의 상당부분을 컴파일러가 보장해줄 수 있다. 자바같은 정적 언어의 장점을 최대한 살릴 수 있는 것이다.

 

아래는 enum을 사용하여 사칙 연산을 정의한 코드이다.

public enum Calc {
    SUM, SUBTRACT, MULTIPLY, DIVIDE
}

...

public int calc(Calc op, int a, int b) {
    switch(op) {
        case SUM: return a + b;
        case SUBTRACT: return a - b;
        case MULTIPLY: return a * b;            
        case DIVIDE: return a / b;
        default: throw new UnsupportedOperationException();
    }
}

...

int result = calc(Calc.DIVIDE, 10, 3);

 

위와 같이 enum을 단순한 데이터 타입으로만 사용한다면 switch문 같은 장황한 비교 구절이 코드 곳곳에 존재하게될 것이다. 그리고 enum에 element가 추가되기라도 한다면 여기저기 산재해 있는 참조 코드를 찾아서 해당 값을 추가해야 할 것이다. 이것은 데이터와 행동이 분리되어 있기 때문이다. 객체지향 관점에서 class는 데이터 + 행동이다. 그리고 enum은 엄밀하게 말하면 class이기 때문에 enum에도 행동을 정의할 수 있다. 간단한 계산 로직은 enum 내에서 제공해준다면 코드의 응집도(cohesion)는 올라가고 결합도(coupling)는 감소할 것이다.

 

아래는 연산 코드가 enum 타입에 함께 구현된 예제이다. MOD 연산이 추가되었지만 코드 수정은 enum Calc 부분으로 제한되었다.

public enum Calc {
    SUM, SUBTRACT, MULTIPLY, DIVIDE, MOD;

    public int calc(int a, int b) {
    	return calc(this, a, b);
    }
    
    public static int calc(Calc op, int a, int b) {
        switch (op) {
            case SUM: return a + b;
            case SUBTRACT: return a - b;
            case MULTIPLY: return a * b;
            case DIVIDE: return a / b;
            case MOD: return a % b; // 추가된 항목
            default: throw new UnsupportedOperationException();
        }
    }
}

...

int result1 = Calc.DIVIDE.calc(10, 3);
int result2 = Calc.calc(Calc.MOD, 10, 3);

 

Java 8 부터는 펑션과 람다를 지원하기 시작하였다. enum에서도 람다와 펑션을 활용하면 훨씬 간소화된 코드를 만들 수 있을 것이다. 이런 기능은 적절하게 사용하는 것이 좋을 것 같다. 과도한 펑션 사용은 가독성과 유지보수성을 떨어뜨릴 가능성이 크기 때문이다. 무엇 보다도 fp(Functional Programming)를 아직 접해보지 않은 개발자가 많다.

 

public enum Calc {
    SUM((a, b) -> a + b),
    SUBTRACT((a, b) -> a - b),
    MULTIPLY((a, b) -> a * b),
    DIVIDE((a, b) -> a / b),
    MOD((a, b) -> a % b);

    private BiFunction<Integer, Integer, Integer> calcFunction;

    Calc(BiFunction<Integer, Integer, Integer> calcFunction) {
        this.calcFunction = calcFunction;
    }

    public int apply(int a, int b) {
        return calcFunction.apply(Integer.valueOf(a), Integer.valueOf(b));
    }

    public static int apply(Calc op, int a, int b) {
        return op.apply(a, b);
    }
}

...

int result1 = Calc.DIVIDE.apply(10, 3);
int result2 = Calc.apply(Calc.MOD, 10, 3);

 

사실 enum이 나온 것은 한참 오래 전이지만 전혀 사용을 안하는 자바 개발자도 꽤 있는 것으로 안다. enum을 사용하지 않는다고 해서 구현이 불가능한 코드는 없기 때문이다. 하지만 견고하고 클린한 코드를 작성하기 위해서는 언어에서 지원하는 새로운 기능들을 적극 활용할 필요가 있다. 일단 보기가 좋다.

Comments