본문 바로가기
Java

Effective Java #02 private 생성자나 enum 자료형은 싱글턴 패턴을 따르도록 설계하라.

by NaHyungMin 2016. 4. 13.

싱글턴은 객체를 하나만 만들 수 있는 클래스를 뜻한다.


JDK 1.5 이전에는 싱글턴을 구현하는 방법이 두 가지라고 한다. (현재 글을 쓰고 있는 시점에 JDK 버전은 1.8까지 나온 것 같다.)


1
2
3
4
5
6
7
//public final 필드를 사용한 싱글턴
public class Elvis{
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {...}
    
    public void leaveTheBuilding() { ...}
}
cs


1
2
3
4
5
6
7
8
9
//정적 팩터리를 이용한 싱글턴
public class Elvis{
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() {...}
    
    public static Elvis getInsctance() { return INSTANCE; }
 
    public void leaveTheBuilding() { ...}
}
cs


이 두가지 모두 리플렉션(reflection) 기능을 통해 private 생성자를 호출할 수 있다.

더불어 최신 JVM에서는 두 가지 성능이 거의 비슷하다고 한다.

다만 펙터리 메서드를 사용하는 한 가지 장점은 API를 변경하지 않고도 싱글턴 패턴을 포기할 수 있다는 것이다.


다음은 리플렉션을 통한 private 생성자를 호출하는 방법이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.lang.reflect.Constructor;
 
public class PrivateInvoker{
    public static void main(String[] args) throws Exception{
        //리플렉션과 setAccessible 메서드를 통해 private로 선언된 생성자의 호출권한을 획득한다.
        Constructor<?> con = Private.class.getDeclaredConstructors()[0];
        con.setAccessible(true);
        Private p = (Private)con.newInstance();
    }
}
 
class Private{
    private Private(){
        System.out.println("hello");
    }
}
cs

위에 방법으로 구현된 싱글턴 클래스를 직렬화 가능(Serializable) 클래스로 만들려면 클래스 선언에 implements Serializable만
추가하는 것으로는 부족하다.
싱글턴 특성을 유지하기 위해서는 모든 필드를 transient로 선언하고 readResolve 메서드를 추가해야 한다.
그렇지 않으면 serialize된 객체가 역직렬화(deserialize)될 때마다 새로운 객체가 생겨난다고 한다.
이 문제를 막으려면 다음과 같은 메서드를 추가해야 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//정적 팩터리를 이용한 싱글턴
public class Elvis{    
    private static final Elvis INSTANCE = new Elvis();    
    private Elvis() {...}
       
    public static Elvis getInsctance() { return INSTANCE; }     
 
    public void leaveTheBuilding() { ...}
 
    private Object readResolve(){
        //동일한 Elvis 객체가 반환되도록 하드는 동시에, 가짜 Elvis 객체는 가비지 컬렉터가 처리하도록 한다.
        return INSTANCE;
    }
}
 


cs


보통 C#에서는 enum은 다음과 같이 코딩하며 사용했다.


1
2
3
4
5
6
enum State
{
    Normal,
    Good,
    VeryGood
}
cs

자바에서는 enum을 클래스처럼도 사용 가능한 것 같다.

1
2
3
4
5
6
//Enum 싱글턴
public enum State {
    INSTANCE("객체");
 
    public void leaveTheBuilding() { ... }
}
cs

원소가 하나뿐인 enum 자료형이야말로 싱글턴을 구현하기 가장 좋은 방법이다.

자바와 C#의 차이..