Whiteship's Note


EJ2E Item 3. 싱글톤 속성은 private 성성자 또는 enum 타입으로

Java : 2008.10.28 09:41


싱글톤은 객체를 딱 한번만 생성하는 클래스.

1.5 전에 싱글톤을 구현하는 방법은 두 개였다. 둘 다 private 생성자와 public static 멤버를 노출한다.

첫 번째 방법: public static filed

public class Elvis {
  public static final Elvis INSTANCE = new Elvis();
  private Elvis(){...}
  ...
}

private 생성자는 딱 한 번 static final 필드인 Elvis.INSTANCE를 초기화할 때만 호출한다. 한 가지 주의 할 것이 있는데...
a privileged client can invoke the private constructor reflectively
(Item 53) with the aid of the AccessibleObject.setAccessible method
이 부분 잘 모르겠슴.

두 번째 방법: public static factory method

public class Elvis {
  private static final Elvis INSTANCE = new Elvis();
  private Elvis(){...}
  public static Elvis getInstance() { return INSTANCE; }
  ...
}

위에서 잘 모르는 부분의 문제가 이렇게 하면 발생하지 않아. (OSAF도 이런식으로 싱글톤을 만들었쥐.) 좀 더 유연하게 변경할 수 있다. Generic 타입을 고려할 수 있다.(Item 27) 이것도 모르겠군.

위의 두 가지 방법은 Serializable 하지 못한다. (static 필드는 직렬화 안 되니깐) 따라서 모든 객체 필드를 transient로 선언하거나 readResolve 메소드(Item 77)를 제공해야 한다.

세 번째 방법: Enum singleton

1.5 부터 사용할 수 있는 싱글톤 구현 방법. 캬.. 드디어 새로운 방법 등장이구나.

public enum Elvis {
  INSTANCE;
  ...
}

이 방법은 기능적으로는 public filed 접근 방법과 동일하지만, 간결하고, 여러 객체를 생성할 여지도 없으며, 직렬화도 제공한다는 점에서 차이가 있다.
a single-element enum type is the best way to implement a singleton
저자가 이 정도로 권장할 정도니까.. 사용해 봐야겠다.
top

  1. Favicon of http://benelog.egloos.com BlogIcon benelog 2008.11.03 11:20 PERM. MOD/DEL REPLY

    "a privileged client can invoke the private constructor reflectively
    (Item 53) with the aid of the AccessibleObject.setAccessible method"

    이 부분은 java.lang.reflect.Constructor 클래스도 java.lang.reflect.AccessibleObject의 하위 클래스이니 setAccessible(true) 해 놓고 Reflection을 사용하면 private 생성자라도 외부에서 호출할 수 있기 때문에 쓴 말 같은데요, 사실 그렇게 까지 private 생성자를 호출해서 얻는 이득(?)이 있다거나, 특정 클래스를 남용할 수 있는 사례는 잘 생각나지는 않네요.

    전 예전에 테스트 coverage를 한 번 100% 만들어보고 싶어서 reflection으로 private 생성자를 호출했었는데, 그래도 100%는 안 떴었던 기억이 있기는해요 ;

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2008.11.03 12:20 신고 PERM MOD/DEL

    음.. 그렇군요. 그렇게 해서 객체를 만들면 객체가 두 개 이상이 될 수 있다는 문제로군요. 우왕..

    감사합니다. :)

Write a comment.


Singletons and lazy loading

Design Pattern : 2007.01.27 10:40


참조 : http://www.oreillynet.com/onjava/blog/2007/01/singletons_and_lazy_loading.html?CMP=OTC-FP2116136014&ATT=Singletons+and+lazy+loading

싱글톤 패턴이 클래스 달랑 하나여서 제일 간단해 보이는 디자인 패턴임에도 불구하고 Lazy Loading 또는 초기화 지연 기법을 사용하려고 하면 굉장히 복잡해 지네요. 그리고 동기화 문제도 생각하면 syncronized가 필수가 됩니다. 그래서 보통은 아래 처럼 구현을 하죠.

class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        return (instance != null) ? instance : (instance = new Singleton())
    }
}

하니면 초기화 지연 기법을 포기하고 아예 처음 변수를 선언할 때 객체도 만들어 버립니다.

class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        return instance;
    }
}

아니면 Head First Design Pattern에도 살짝 나왔던 것 같은데 2중 체크 라는 것을 합니다. 하지만 이것도 되는 것 처럼 보일 뿐 제대로 동작하지 않습니다.

// Correct Double-Checked Locking for 32-bit primitives
class Foo {
    private int cachedHashCode = 0;
    public int hashCode() {
        int h = cachedHashCode;
        if (h == 0)
        synchronized(this) {
            if (cachedHashCode != 0) return cachedHashCode;
            h = computeHashCode();
            cachedHashCode = h;
        }
        return h;
    }
    // other functions and members...
}

예제 코드가 좀 거시기 하지만 얼추 이와 비슷한 코드들이 동기화 관련된 부분에서 자주 보입니다. 이게 제대로 동작하지 않는 이유는 빨간 색 코드를 호출해서 h가 참조할 객체가 만들어 지는 도중에 녹색 조건 문에 다른 쓰레드가 걸려서 다 만들어 지지도 않은 객체의 레퍼런스를 가져갈 수 있기 때문인 것 같은데 이 것에 대한 자세한 내용은 여기를 참조하세요.

Anyway... 그럼 어떻게 하면 좀더 간단하고 빠르게(syncronized를 쓰지 않고) 싱글톤과 초기화 지연 기법을 사용할 수 있을까 하는게 참조한 글의 주제 였는데요.

public class Singleton {

    static class SingletonHolder {
        static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

}

이 코드는 위키피디아에 Initialization on Demand Holder를 참조 했다고 합니다. 위 코드를 보시면 먼저 Syncronized가 사라진걸 볼 수 있구요. 따라서 멀티 쓰레드 환경에서 매우 빠르게 동작할 것 같습니다. 그리고 초기화 지연은 어떻게 보장을 할까요. getInstance() 메소드가 호출되면 그 때 static inner 클래스인 SingletonHolder가 JVM에 로딩 되게 되고 그 때서야 객체는 만들어 지게 되는거죠. 그리고 그 이후에는 static이니까 싱글톤.. 단일 객체를 보장하게 되는 겁니다.

'Design Pattern' 카테고리의 다른 글

데코레이터(Decorator) 패턴  (0) 2008.10.01
프록시(Proxy) 패턴  (0) 2008.09.26
JUnit 공부하자.  (0) 2008.09.01
H.F.OOAD 5장 문제  (0) 2007.11.20
상위 클래스 보다는 인터페이스를...  (4) 2007.08.31
Singletons and lazy loading  (2) 2007.01.27
객체지향 디자인 원칙  (2) 2006.12.11
Losely Coupled를 활용하라.  (0) 2006.12.11
상속보다는 구성을 활용한다.  (0) 2006.12.11
바뀌는 부분을 캡슐화 한다.  (0) 2006.12.11
State Pattern 예제  (2) 2006.12.10
top

  1. Favicon of http://decoder.tistory.com BlogIcon decoder 2007.01.27 13:11 PERM. MOD/DEL REPLY

    역시 빠르시군요 :)
    오늘 아침에서야 제가 소화할만한 아티클이 올라왔나 했더니. ^^;

    Favicon of http://whiteship.tistory.com/ BlogIcon 기선 2007.01.27 23:02 PERM MOD/DEL

    ㅎㅎ먼저 먹어서 죄송~

Write a comment.