자 이전 Enum에서 업그레이드 판인 Enum에서 함수형 인터페이스의 활용을 알아볼 것이다.
처음보고 굉장히 놀라버렸지 뭐얌
일단.. 함수형 인터페이스가 뭔지도 잘 모르기 때문에 함수형 인터페이스부터 보고 가자.
함수형 인터페이스란 추상 메서드가 오직 하나인 인터페이스이다.
추상 메서드가 오직 하나 라는 말은 디폴트 메서드나 스태틱 메서드는 여러개 있어도 괜찮다는 뜻
이번 글에서 알아보고자 하는 것은 함수형 인터페이스 + Enum의 활용이다.
간단히 넘어가보자
인터페이스 | 메서드 | 설명 |
---|---|---|
Predicate | boolean test(T t) | 매개변수를 조사한 후 논리값 반환 |
Consumer | void accept(T t) | 매개변수를 받아서 소비함 반환값 없음 |
Function<T,R> | R apply(T t) | 매개변수를 반환값 타입으로 변환 후 반환 |
Supplier | T get() | 데이터를 공급하는 역할, 지정된 반환 값을 반환 |
두 개의 인자를 받는 BiPredicate, BiConsumer, BiFunction 등등
더 많은 인터페이스를 제공하지만 이번엔 여기까지만 알아보고,
추후에 사용할 인터페이스가 있다면 이 글을 토대로 활용해보자.
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을 정의하는 것을 추천한다고 한다.
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
하 . 어렵다 자바 !!
가보자가보자!