Java - Snake Game 과 Enum

Seok-Hyun Lee·2021년 7월 18일
0

Toy_Project

목록 보기
1/1

개요, Enum을 사용해보자

이번에 새롭게 해본 프로젝트는 그 유명한 Snake Game이다. 토이 프로젝트 중에 아마 가장 유명하고 프로그래밍 튜토리얼로도 자주 다뤄지는 게임이다. 하지만, 이번에 이 프로젝트를 하게 된 이유는 사실 Enum 을 좀 다뤄보기 위해서이다.

Enum을 어떤 예제로 연습을 좀 해볼까하다가, 게임에서는 설정이나 방향은 주로 상수로 다뤄지기 때문에 직접 만들어보기로 했다. 그래서 SnakeGame을 만들게 되었다.

웹상에 snake game 관련한 예제가 많이 있으니 로직이나 전반적인 소스 코드에 대해선 언급하지 않겠다.

목표

Enum 활용

이번 프로젝트의 목표는 enum 이니 만큼 설정과 방향을 enum으로 만들어서 활용해보기로 해서 아래와 같이 설정하였다.

public class Panel extends JPanel implements ActionListener {

    // Enum 활용 연습
    // 창 크기, 격자 정보 등
    private enum Configure {
        SCREEN_WIDTH(800),
        SCREEN_HEIGHT(800),
        CELL_SIZE(40),
        CELL_LINE(getCellLine()),
        CELL_TOTAL(getCellTotal()),
        DELAY(70);

        int size;
        Configure(int size){
            this.size = size;
        }

        private static int getCellTotal(){
            return SCREEN_HEIGHT.size * SCREEN_WIDTH.size / CELL_SIZE.size;
        }

        private static int getCellLine(){
            return SCREEN_HEIGHT.size / CELL_SIZE.size;
        }
    }

    // Enum 활용 연습
    // 방향
    private enum Direction {
        RIGHT,
        LEFT,
        UP,
        DOWN
    }

    final int[] x = new int[Configure.CELL_TOTAL.size];
    final int[] y = new int[Configure.CELL_TOTAL.size];

    Direction direction = Direction.RIGHT;
    ...
}

enum은 총 두 개로 게임창과 격자 정보에 대한 enum인 Configure와 방향에 대한 enum인 Direction이 있다. 각 특징은 아래와 같다.

  • Configure 는 필드와 메서드 그리고 생성자가 있는 Enum
  • Direction 은 enum 값만 있는 Enum

느낀점

우선, 개별 데이터를 저장할 수 있는 Enum인 Configure 를 작성하면서 처음에 느꼈던 것은 불편함이다. 가장 주된 이유는 호출의 번거로움이었다. 아무래도 예제 자체가 가벼운 탓에 이 방식을 활용할 만큼의 좋은 상수 집합을 만들지 못한 이유에서 나온 느낀점인 것 같다. 그럼에도 얘기를 하자면, 아래와 같은 이유이다.

  • 멤버 변수에 static final 로 선언하는 것과 달리 Configure.CELL_SIZE.size처럼 특정 값을 호출하기 번거롭다

아래의 코드가 더 직관적이면서도 코드 작성하기도 편리하고 오류를 방지하기 좋다.

private static final CELL_TOTAL = 40;

final int[] x = new int[CELL_TOTAL];

두번째로, 일반적인 Enum인 Direction 을 사용한 느낀 점은 편리함과 가독성이다.
아래는 Direction 레퍼런스인 direction을 switch 문에서 사용하는 예제이다.

        switch(direction) {
            case UP:
                y[0] = y[0] - Configure.CELL_SIZE.size;
                break;
            case DOWN:
                y[0] = y[0] + Configure.CELL_SIZE.size;
                break;
            case RIGHT:
                x[0] = x[0] + Configure.CELL_SIZE.size;
                break;
            case LEFT:
                x[0] = x[0] - Configure.CELL_SIZE.size;
                break;
        }

enum을 사용하지 않은 상황에서 조건에 맞는 case를 설정하는 것은 로직은 같을지 모르겠지만 코드 가독성이 enum을 사용하는 경우보다 떨어지는 것을 확인할 수 있었다. 그리고 IDE에 따라 다를 수 있지만 direction이 Direction 을 참조하는 것이다 보니 case 별 enum value를 자동 완성해주어서 편리하게 코드를 짤 수 있었다.

개선 및 결론

그래서 그나마 호출을 편리하게 할 수 있는 방법을 찾다보니 아래와 같은 방법을 찾을 수 있었다.

  1. 패키지를 설정(패키지가 없으면 진행 불가, 에러 발생)
  2. import static 활용 ( 변수이름이나 메소드명을 클래스명 없이 호출 가능 )

아래는 활용한 예제이고 결과이다

import static game.snake.Panel.Configure.*;
import static game.snake.Panel.Direction.*;

...

final int[] x = new int[CELL_TOTAL.size];
final int[] y = new int[CELL_TOTAL.size];

Direction direction = RIGHT;

...

확실히 가독성도 좋아지고 호출할 때의 번거로움도 약간은 해소되었다.하지만, CELL_TOTAL.size 처럼 CELL_TOTAL로 바로 int 값을 얻어오는 것이 아니라 . 연산자로 size의 값을 가져와야 하는 번거로움이 있다. 참고로, enum 값이 겹칠 경우 enum type을 명시해서 컴파일 에러를 피하자.

결론은, enum은 확실히 가독성을 좋게 만들어 준다. 하지만, enum 값에 데이터를 넣는 방식으로 Enum 을 정의할 때 좀 더 신중하게 결정할 필요가 있다. 개인적으로는 아래와 같은 생각이 들었다.

  • 데이터를 가진 enum으로 만들고자 할 때, 가지는 데이터가 하나이고 특정 클래스에 종속적이면 private final 추천
  • 상수 값 수정/추가 등 유지 보수가 자주 일어날 때는 enum을 통한 관리 추천
profile
Arch-ITech

0개의 댓글