'enum'에 해당되는 글 2건

  1. 2016.08.13 enum의 활용법
  2. 2016.08.07 Enum Factory Method 패턴

enum의 활용법

4.JAVA 2016. 8. 13. 18:34

C언어에서 enum은 단순히 상수형 변수 역할에 지나지 않았다. 하지만 Java에서는 매우 다른 특성들을 지니고 있다. 이 특성들 중에는 특별한 것들도 있어서 기존과는 다른 여러 방식으로 enum을 활용할 수 있다.


먼저 enum의 실제 타입부터 알아보자.


enum의 실제 타입

enum Type{ // abstract class

    ADD,    // public static final class ADD extends Type{}

    SUB;    // public static final class SUB extends Type{}

}


이처럼 기본적으로 enum은 추상 클래스이다. 그리고 그 하위에 선언된 각 열거형 변수는 실제로는 변수가 아니고 enum의 타입을 상속 받은 하위 클래스이다. 이 하위 클래스는 외부에 노출되어 있고 생성할 필요가 없으며 런타임에 교체가 불가능하므로 public static final 타입을 갖는다.




enum은 기본적으로 추상 클래스이기는 하나 다른 클래스로부터 상속을 받지는 못한다. 하지만 interface는 상속을 받을 수 있다. 따라서 다음과 같은 형태로 구현이 가능하다.


enum에 인터페이스 상속 받기

interface Interface{

    public void api();

}

enum Type implements Interface{

    ADD{

        public void api(){ System.out.println("ADD api"); }

    },

    SUB{

        public void api(){ System.out.println("SUB api"); }

    };

}


이 인터페이스 상속 방법은 생각보다 강력하다. 이 특성을 이용해서 디자인 패턴에 나오는 수많은 패턴들을 enum을 통해 구현할 수 있다. enum이 public static final 이라는 점은 Singleton과 유사한 특성을 지니고 있다. 따라서 중복 생성이 안되면서도 일반 클래스 기능을 할 수도 있고, 필요한 때에는 enum의 특성을 활용할 수도 있다. 이 강력한 특성 때문에 Singleton 패턴, Abstract Factory 패턴, Factory Method 패턴, State 패턴, Strategy 패턴, Visitor 등 다양한 패턴의 enum 버전이 있다. 이들에 대해서는 디자인 패턴 항목에서 다룰 예정이다.




추상 클래스라면 함수를 선언할 수도 있어야 한다. 아래와 같이 함수 선언이 가능하다.


enum에서 함수 선언하기

enum Type{

    ADD,

    SUB;

    public void api(){ System.out.println("api()"); }

}




추상 클래스이기 때문에 추상 메소드의 선언도 가능하다. interface를 상속 받을 수 있다는 점에서도 이 점은 유추해 낼 수 있다.


enum에서 추상 메소드 선언하기

enum Type{

    ADD{

        public void api(){ System.out.println("ADD api"); }

    },

    SUB{

        public void api(){ System.out.println("SUB api"); }

    };

    abstract public void api();

}




같은 맥락에서 static 메소드 역시 가능하다. 실용적인 예제를 위해서 하위 타입의 개수를 알아내는 함수로 해보자.


enum에서 static 메소드 선언하기(하위 타입의 개수 알아내기)

public static int size(){ return values().length; }


보통은 Type.values().length를 통해 꺼내오기도 하지만 가독성 면에서 Type.size()를 호출하는 것이 더 나아 보인다.




클래스라면 생성자도 선언할 수 있어야 할 것이다. 생성자 선언은 아래와 같다.


enum에서 생성자 선언하기(enum 클래스를 인덱스로 활용하기)

enum Type{

    ADD(0),// 생성자에게 필요한 인자는 () 안에 넣는다.

    SUB(1); //

    int value;

    private Type(int value){ this.value = value; }   // 이것이 생성자.

    public int value(){ return value; }

}


역시 활용성을 위해서 enum 클래스를 인덱스로 활용할 수 있도록 value 라는 int 형 값을 생성시에 인자로 받도록 했다. Java의 enum은 클래스이기 때문에 C언어에서 처럼 값으로 활용할 수가 없다. 대신에 이처럼 생성자를 통해 인자로 받은 값을 가지고 있다가 값이 필요한 경우 value() 함수를 호출함으로써 값으로도 사용이 가능하다. 이 활용법은 "Effective Java" 라는 책에 수록된 내용이다. 생성자가 private인 것에 주목해야 한다. enum은 외부에서 생성이 불가능하기 때문에 생성자는 항상 private으로 선언해 주어야 한다.




다음은 문자열로 enum을 알아내는 함수이다. enum이 클래스라는 것은 이미 이야기 하였다. 따라서 하위 타입들도 모두 toString() 함수를 가지고 있는데, 그 결과 값은 기본적으로 자신의 선언된 이름과 같다. ADD.toString()은 "ADD" 값이 결과값이다. 이러한 특성은 매우 유용한데, 특히 데이터베이스에 저장할 때 그렇다. DB의 가독성 측면에서 문자열을 활용한 경우에 문자열을 입력받아 타입을 리턴하도록 하면 코드 상에서 문자열 대신 enum을 활용할 수 있으므로 코딩이 편리해진다.


enum 에서 문자열로 enum의 타입을 알아내기

public static Type getTypeByString(String str){

    for(Type each : values()){

        if(each.toString().equals(str)) return each;

    }

    return null;

}



enum 타입이 제공하는 기본 함수로 enum의 순서를 알 수 있는 함수가 있다.


public int ordinal();


위 ordinal() 이라는 함수인데 이 함수는 선언된 enum의 하위 타입이 몇 번째 순서로 선언되었는지를 알 수 있다.(순서의 시작 값은 0이다.) 가령 위에서 선언한 ADD 타입의 경우 0이 리턴되고, SUB의 경우 1이 리턴된다. 이 특성을 이용하면 enum을 인덱스로도 활용이 가능하다.



Java에서의 enum은 열거형의 특성과 클래스의 특성을 함께 가지고 있다는 장점이 있다. toString() 함수를 가지고 있다는 것만으로도 디버깅을 얼마나 쉽게 만들어 주는지 모른다. 그 밖에도 데이터 베이스와의 연동, switch-case 문에 대한 활용, 인터페이스 상속을 활용한 디자인 패턴 등 다양한 곳에 활용할 수 있다.

Posted by 이세영2
,

Enum Factory Method 패턴은 Factory Method 패턴의 단점을 보완하기 위한 패턴이다.

기본적으로 Factory Method 패턴과 마찬가지로 객체의 생성을 담당하는 메소드를 구현하는 패턴이다. 이와 함께 Factory Method를 구현한 객체를 생성하기 위해 Singleton을 사용해야 하는 문제점을 Enum의 특성을 이용하여 해결한다.


Enum Factory Method 패턴(Java에서만 가능)

public enum EnumFactoryMethod {

    RECTANGLE{

        protected Shape createShape(){return new Rectangle();}

    }

    ,CIRCLE{

        protected Shape createShape(){return new Circle();}

    }

    ;

    public Shape create(Color color){

        Shape shape = createShape();

        shape.setColor(color);

        return shape;

    }

    abstract protected Shape createShape();

    public static void main(String[] args) {

        EnumFactoryMethod.RECTANGLE.create(Color.BLACK);

        EnumFactoryMethod.CIRCLE.create(Color.WHITE);

    }

}



Enum 타입 자체가 public static final 이기 때문에 생성을 위임 받은 객체에 대한 중복 생성이 불가하고, Singleton을 굳이 구현하지 않아도 단일한 객체만 생성됨이 보장된다.


Enum의 이러한 특성은 다른 패턴들에도 응용이 될 수 있는데 이는 이후 포스팅을 통해 살펴보도록 하겠다.

Posted by 이세영2
,