함수형 인터페이스와 Enum

Jaca·2021년 11월 18일
1

자 이전 Enum에서 업그레이드 판인 Enum에서 함수형 인터페이스의 활용을 알아볼 것이다.

처음보고 굉장히 놀라버렸지 뭐얌

일단.. 함수형 인터페이스가 뭔지도 잘 모르기 때문에 함수형 인터페이스부터 보고 가자.

함수형 인터페이스

함수형 인터페이스란 추상 메서드가 오직 하나인 인터페이스이다.
추상 메서드가 오직 하나 라는 말은 디폴트 메서드나 스태틱 메서드는 여러개 있어도 괜찮다는 뜻

이번 글에서 알아보고자 하는 것은 함수형 인터페이스 + Enum의 활용이다.

간단히 넘어가보자

Java에서 제공하는 함수형 인터페이스

인터페이스메서드설명
Predicateboolean test(T t)매개변수를 조사한 후 논리값 반환
Consumervoid accept(T t)매개변수를 받아서 소비함 반환값 없음
Function<T,R>R apply(T t)매개변수를 반환값 타입으로 변환 후 반환
SupplierT get()데이터를 공급하는 역할, 지정된 반환 값을 반환

두 개의 인자를 받는 BiPredicate, BiConsumer, BiFunction 등등
더 많은 인터페이스를 제공하지만 이번엔 여기까지만 알아보고,
추후에 사용할 인터페이스가 있다면 이 글을 토대로 활용해보자.

Enum + 함수형 인터페이스

Enum과 함수형 인터페이스가 만나면 어메이징한 일이 일어나는데..

만약 단순 계산 식을 입력 받아 계산하는 계산기가 있다고 하자.
Enum을 사용하지 않고 코드를 작성하면 어떻게 될까?

public class Calculator { 
    private final Double valueA; 
    private final String operator; 
    private final Double valueB; 

    public Calculator(@NonNull String text) { 
        String[] splitText = text.split(" ");
        int idx = 0;
        
        this.valueA = Double.valueOf(splitText[idx++]); 
        this.symbol = splitText[idx++]; 
        this.valueB = Double.valueOf(splitText[idx++]); 
    } 

    public Double calculate() throws CloneNotSupportedException { 
        switch (operator) { 
            case "+": 
                return valueA + valueB; 
            case "-": 
                return valueA - valueB; 
            case "*": 
                return valueA * valueB; 
            case "/": 
                return valueA / valueB; 
        } 
    throw new Exception("symbol is not supported " + this.operator); 
    } 
}

아주 무난하다.
하지만,
계산하는 부호들('+', '-', '*', '/') 들이 여기서만 사용한다는 전제가 보장되지 않는다.
때문에, 여러 곳에서 중복 코드가 추가적으로 계속 발생할 수 있다.

계산 부호들이 추가되었을 때, 혹은 계산 부호에 의한 계산 방법이 변경되었을 때, 해당 정책에 대한 코드가 여러 곳에서 관리되고 있다면, 리팩터링 하는 것이 매우 어렵다.

그래서 특정 블록에서만 type에 의해서 ( if, switch case )를 사용해야 하는 경우가 생긴다면 반드시 ENUM을 정의하는 것을 추천한다고 한다.

Enum을 활용하자.

public enum CalculatorOperator {
    PLUS("+", (a, b) -> a + b), 
    MINUS("-", (a, b) -> a - b), 
    DIVISION("/", (a, b) -> a / b), 
    MULTIPLICATION("*", (a, b) -> a * b), 
    UNKNOWN(null, null); 

    private final String operator; 
    private final BiFunction<Double, Double, Double> calculateFunc; 

    CalculatorSymbol(String operator, BiFunction<Double, Double, Double> calculateFunc) { 
        this.operator = operator; 
        this.calculateFunc = calculateFunc; 
    }

    public Double calculate(Double a, Double b) { 
        return this.calculateFunc.apply(a, b); 
    } 

    public static CalculatorOperator findOperator(String text) { 
    return Arrays.stream(CalculatorOperator.values())
        .filter(type -> type.operator != null)
        .filter(type -> text.contains(type.operator))
        .findFirst()
        .orElse(CalculatorOperator.UNKNOWN); 
    } 
}

기존 Enum의 속성에 BIFunction(두 개의 double 인자를 받아서 , double을 리턴) 함수형 인터페이스 필드를 추가한다.

Enum의 속성에 함수 인터페이스에 람다를 할당할 수 있다.
즉, 람다 그 자체를 인스턴스라고 생각하면 된다.

실제 타입의 속성 안에 계산 식이 포함되니, 실제 사용하는 로직에서 타입별로 기능 코드를 작성하는 것에 비해서 굉장히 자연스러워지고,
타입이 추가되었을 때 다른 참조 클래스들에 대해서 추가적인 수정이 필요 없으니, 유지보수 측면에서도 유리하다.

그러면 이 Enum 클래스를 통해 메인 클래스는 어떻게 바뀌었을까?

public class Calculator {
    private final Double valueA; 
    private final CalculatorOperator operator; 
    private final Double valueB; 

    public Calculator(@NonNull String text) { 
        String[] splitText = text.split(" "); 
        
        this.valueA = Double.valueOf(splitText[0]); 
        this.operator = CalculatorSymbol.findSymbol(text); 
        this.valueB = Double.valueOf(splitText[2]); 
    } 

    public Double calculate() { 
        return this.operator.calculate(this.valueA, this.valueB); 
    } 
}

WOW AMAZING

하 . 어렵다 자바 !!
가보자가보자!

profile
I am me

0개의 댓글

관련 채용 정보