[Java/객체지향] Enum

양성욱·2023년 9월 18일
0
post-thumbnail

이 글은 프로그래머스 - 실무 자바 개발을 위한 OOP와 핵심 디자인 패턴 강의를 정리한 내용입니다.

Enum은 Enumulation의 줄임말로 열거형이라고 부릅니다. 가장 기본적인 형태는 다음과 같습니다.

CalculateTypeV1

public enum CalculateType {
		ADD, MINUS, MULTIPLY, DIVIDE
}

Enum은 단순히 데이터의 타입을 정의할 때 사용한다고 생각하기 쉽지만, 간단한 문법으로 강력한 다형성을 제공해줍니다.

Enum을 단순 타입 정의로만 활용

위에서 정의한 Enum을 단순히 데이터 타입 정의 정도로만 사용한다면 다음과 같이 활용할 수 있습니다.

CalculateCommandV1

public class CalculateCommand {
    private CalculateType calculateType;
    private int num1;
    private int num2;

    public CalculateCommand(CalculateType calculateType, int num1, int num2) {
        this.calculateType = calculateType;
        this.num1 = num1;
        this.num2 = num2;
    }

    public CalculateType getCalculateType() {
        return calculateType;
    }

    public int getNum1() {
        return num1;
    }

    public int getNum2() {
        return num2;
    }
}

ClientV1

public class Client {
    public int someMethod(CalculateCommand calculateCommand) {
        CalculateType calculateType = calculateCommand.getCalculateType();
        int num1 = calculateCommand.getNum1();
        int num2 = calculateCommand.getNum2();

        int result = 0;

        if(calculateType.equals(CalculateType.ADD)) {
            result = num1 + num2;
        } else if(calculateType.equals(CalculateType.MINUS)) {
            result = num1 - num2;
        } else if(calculateType.equals(CalculateType.MULTIPLY)) {
            result = num1 * num2;
        } else if(calculateType.equals(CalculateType.DIVIDE)) {
            result = num1 / num2;
        }

        return result;
    }
}

Client코드를 보면 각각의 CalculateType에 대해서 각기 다른 분기문이 실행되는 로직을 확인할 수 있습니다.
someMethod는 파라미터로 받은 CalculateTypenum1, num2 파라미터의 값에 따라 다른 결과를 만들어 반환합니다.

위 코드에서 눈여겨 볼 부분은 CalculateType에 따라서 코드가 분기되고 있다는 점입니다. 즉, enum이 현재 어떻게 활용되고 있는지에 대한 부분입니다.

MainV1

public class BasicEnumExampleMain {
    public static void main(String[] args) {
        CalculateCommand calculateCommand = new CalculateCommand(
                CalculateType.ADD,
                100,
                3
        );  // 보통은 클라이언트가 요청할 때 보내준 데이터를 사용

        Client client = new Client();
        int result = client.someMethod(calculateCommand);

        System.out.println(result);
    }
}

Main로직을 살펴보면 CalculateCommand의 생성자를 통해 적절한 CalculateTypenum1, num2를 포함한 CalculateCommand 인스턴스를 생성해서 ClientsomeMethod 인자로 넘겨주는 것을 확인할 수 있습니다.

👀 물론 위 로직은 예시를 위해 직접 필요한 데이터들을 생성해주었고, 일반적으로는 프론트엔드에서 넘어온 데이터를 처리합니다.

이제 코드를 실행해보면 원하는 계산결과가 잘 출력되는것을 확인할 수 있습니다.

result = 103

Enum 좀 더 활용하기 (근데 이제 람다식을 곁들인)

위에서 살펴본 코드를 Enum을 좀 더 활용하여 개선해보겠습니다.

CalculateTypeV2

public enum CalculateType {
    ADD ((num1, num2) -> num1 + num2),
    MINUS ((num1, num2) -> num1 - num2),
    MULTIPLY ((num1, num2) -> num1 * num2),
    DIVIDE ((num1, num2) -> num1 / num2);

    CalculateType(BiFunction<Integer, Integer, Integer> expression) {
        this.expression = expression;
    }

    private BiFunction<Integer, Integer, Integer> expression;

    public int calculate(int num1, int num2) {
        return this.expression.apply(num1, num2);
    }
}

기존 CalculateTypeV1 코드는 단순히 타입만 나열했던것에 비해 V2 코드는 더 복잡해졌습니다. 단순히 타입만 정의한 게 아닌 각 타입에 대한 연산의 행위도 포함하고 있습니다.

Enum에 생성자 코드가 등장하였습니다. 이 생성자는 각 타입의 괄호에 들어가는 값들을 정의해주는 역할을합니다. 이 괄호 부분에 들어간 파라미터가 생성자로 들어온다고 생각하면됩니다.
(자세한건 Java 기본서에 Enum 문법을 더 알아보세요!)

생성자 아래쪽에는 필드가 하나 선언되어있습니다.

BiFunction<a, b, c>은 함수형 인터페이스로 함수를 마치 하나의 타입처럼 다룰 수 있게 해주는 문법입니다.

  • a와 b는 BiFunction을 통해 정의되는 함수의 첫번째, 두번째 파라미터 각각의 타입을 지정해줍니다.
  • c는 BiFuction을 통해 정의되는 함수의 리턴 타입을 지정해줍니다.

calculate 메서드는 인자로 받은 두 정수 num1num2를 각각의 내부 데이터 타입에 정의된 람다식을 사용해 연산한 결과를 반환합니다.

이제 개선된 CalculateTypeV2에 맞춰 클라이언트 코드 역시 변경해보겠습니다.

ClientV2

public class Client {
    public int someMethod(CalculateCommand calculateCommand) {
        CalculateType calculateType = calculateCommand.getCalculateType();
        int num1 = calculateCommand.getNum1();
        int num2 = calculateCommand.getNum2();

        int result = calculateType.calculate(num1, num2);

        return result;
    }
}

ClientV2는 이전 V1보다 코드가 훨씬 단순해졌습니다.

가장 눈에 띄는 변화는 기존 V1에 존재하던 분기문이 사라졌다는 점입니다. 이제 CalculateType에 대한 calculate 메서드만 호출해주면 연산은 enum 코드 안에서 이뤄지기 때문에 더이상 분기문으로 적절한 연산을 Client에서 수행할 필요가 없어졌습니다.

V1 -> V2 변경에서 중요한 포인트

V1 -> V2 변경에서 중요하게 봐야할 포인트는 다음과 같습니다.

  • if & else 같은 분기문이 사라졌습니다. 따라서 이전보다 코드의 가독성이 올라가고 로직이 훨씬 명확해졌습니다.
  • ClientCalculateType에 대한 구체적인 내용을 몰라도됩니다.

두 번째 포인트가 특히 중요합니다.

외부에 존재하던 CalculateType의 연산 로직들이 모두 내부로 들어갔습니다. 결과적으로 관련된 로직들이 모두 enum안에 존재하기 때문에 코드의 응집력이 높아졌다고 얘기할 수 있습니다.


V1ClientCalculateType에 정의된 각각의 타입은 물론 해당 타입이 어떤 연산을 하는지 모두 알고있었습니다. 따라서 CalculateType에 어떤 변화가 생긴다면 Client역시 그 변화에 영향을 받을 가능성이 매우 높습니다.

하지만 V2Client가 더이상 CalculateType의 세부적인 타입들까지 알고있을 필요가 없습니다. 따라서 CalculateType의 변화에 비교적 자유롭고, CalculateType은 이전보다 더욱 추상적인 존재가됩니다.

CalculateType.ADD보다는 CalculateType.calculate()가 더 추상적인 코드입니다.

이처럼 Enum을 잘 활용하면 객체지향적인 코드를 작성하는데 큰 도움을 받을 수 있습니다.

profile
개발의 신이시여... 제게 집중할 수 있는 ㅎ... 네? 맥주요?

0개의 댓글