본문 바로가기

Java

Enum 활용(람다식 사용하기)

3주차 미션이 끝나고 마지막 최종 코딩 테스트를 기다리고 있다. 3주차 미션은 너무 어려웠어서, 코드 리펙토링이 충분히 진행되지 못했다. 그래서 조금 더 코드를 줄이는 방법을 고민한 결과 Enum을 좀 더 고급스럽게 사용하면 코드를 더 줄일 수 있을 것 같았다.

 

Enum은 서로 연관된 상수의 집합이다.

클래스로서 상수들을 정의할 수 있고, 생성자와 메서드를 가질 수 있다.

 

새로운 영감을 받은건 아래 글의 댓글이었다.

okky.kr/article/641946

 

 

나는 enum 클래스를 이렇게 이용하고 있었다.

 

Option은 사용자가 선택하는 번호, getMenu()는 사용자 입력 String을 Option과 비교해 같은 enum상수를 반환하여 컨트롤러에서 사용했다.

 

public class LineMenuController implements Controller{
    private static final LineMenuController lineMenuController = new LineMenuController();

    private LineMenuController() {
    }

    public static LineMenuController getInstance() {
        return lineMenuController;
    }

    public void mappingMenu(Scanner scanner) {
          LineOutputView.printLineManagePage();
          branchBySelectedOption(scanner, LineInputView.selectLineMenu(scanner));
    }

    private void branchBySelectedOption(Scanner scanner, LineMenu selectedOption) {
        if (selectedOption.equals(LineMenu.GO_BACK_TO_MAIN_MENU)) {
            System.out.println();
            return;
        }
        if (selectedOption.equals(LineMenu.LINE_ADD)) {
            LineAddService lineAddService = LineAddService.getInstance();
            lineAddService.lineAddService(scanner);
        }
        if (selectedOption.equals(LineMenu.LINE_DELETE)) {
            LineDeleteService lineDeleteService = LineDeleteService.getInstance();
            lineDeleteService.lineDeleteService(scanner);
        }
        if (selectedOption.equals(LineMenu.LINE_SELECT)) {
            LinesPrintService.showAllLines();
        }
    }
}

문제의 컨트롤러 코드..

 

반환받은 enum상수를 비교하여 해당하는 서비스로 분기하고 있다.

 

이 if문을 이용한 분기처리를 enum자체에 함수형 인터페이스로 넣어버린다면 컨트롤러가 더 깔끔해 질 것이라고 생각했다.

if문에서 Service 객체를 생성하여 리턴하는 것이 아닌 다형성을 이용하여 enum에서 정의된 메소드를 실행하는 방향으로 구상했다.

 

 

 

1. 먼저 무작정 서비스 메서드를 Enum에 넣어봤다.

lineAddService() 메소드는 매개변수로 Scanner하나를 받아 서비스를 처리하는 메소드로 만들었기에, Comsumer 인터페이스를 사용했다.

 

Non-static은 들어갈 수 없다고 한다.

Enum은 상수 즉 static final 형을 모아놓는 곳이다.

정적으로 메모리에 처음 할당되는데, static이 아닌 메서드는 넣을 수 없다는 것이었다.

 

 

 

가장 먼저 든 생각은 'lineAddService메소드와 관련 메소드들을 싹다 static으로 바꿀까?' 였다.

하지만 바로 다음 든 생각은 LineAddService 클래스를 싱글톤으로 사용했다는 것이었다. (메소드와 클래스 이름이 같다니.. 고민이 부족했다.)

 

싱글톤의 getInstance()는 static으로 선언되었으니 서비스 객체 자체를 가지고 있다가 메소드를 실행할 수 있지 않을까 였다.

그래서 바꿨다.

 

getInstance 메서드는 파라미터 없이 LineAddService형 객체를 리턴하기 때문에 함수형 인터페이스 Supplier을 사용했다.

 

 

 

메소드 레퍼런스 방식을 이용하면 더 깔끔하게 표현할 수 있다.

노선 삭제에 대해서도 람다식을 넣었더니, Bad return type이라 한다.

Supplier<LineAddService> 라고 선언해놨으니 LineDeleteService 객체는 타입이 안맞단 소리다.

공통적인 객체로 받아 공통 메소드를 실행하기 위해 인터페이스를 사용하기로 했다.

goToMenu() 는 Java8에서 인터페이스는 default키워드를 이용하여 구현하는 클래스에서 공통적으로 가질 수 있는 메소드를 선언할 수 있어서 사용했다. 기능은 라인 메뉴에서 작업이 끝나면 메인메뉴로 돌아가는.. 뭐 그런 역할이다.

 

아래의 lineService가 추상메서드로 모든 구현클래스들이 구현해야 하는 메소드다.

 

메서드명을 lineAddService에서 인터페이스가 강제하는 lineService로 바꿨더니 컨트롤러에서 오류가 났다.

 

인터페이스를 implements하고 메서드명을 바꿔줬다.

노선을 삭제하는 LineDeleteService클래스, 노선을 출력하는 LinesPrintService에도 똑같이 구현과 메서드명을 바꿔줬다.

 

 

그러면 이제 Enum에 정의된 모든 기능은 LineService라는 인터페이스를 구현한 클래스가 하게 되므로 아래처럼 바꿀 수 있다.

Supplier<LineService>가 된 것을 보라

 

이제는 컨트롤러에서 Enum상수에 정의된 람다식을 실행시킬 수 있는 메소드를 Enum상수에 추가한다.

 

이제는 컨트롤러의 코드를 바꿀 수 있다.

한줄로 줄었다.

 

 

그런데 메뉴를 입력받을 때, 굳이 Enum상수를 받아 비교하지 않고 그냥 String값을 받아 Option과 비교하면 될 것 같다.

getMenu() 메소드를 삭제하고,

사용자에게 그냥 String을 입력받아 execute로 전달하게 한 후

execute 메소드를 LineMenu가 아닌 String으로 받게 바꾸고, filter에서 menu.option을 바꾸어 옵션 필드를 비교하게 하였다.

 

 

 

 

이렇게 자바 8버전의 람다식을 알게되며 리펙토링할 부분이 또 생기게 되는 경험이었다.

'Java' 카테고리의 다른 글

Java Garbage Collection  (0) 2021.03.15
List.of() vs Arrays.asList() vs Collections.unmodifiableList()  (0) 2021.02.08
함수형 인터페이스 정리  (0) 2020.12.18
인터페이스 vs 추상 클래스  (0) 2020.12.18
JAVA8) 스트림 API  (0) 2020.12.11