제 홈페이지의 모든 글은 anti-nhn license에 따릅니다.



Singleton 심화학습


1. static 메쏘드를 가진 클래스를 쓰는 것과의 차이점.


싱글톤을 안쓰고, static 메쏘드를 가진 클래스로 쓰게되면, 클라이언트 코드가 짧아 집니다.

static의 경우.
StaticClass.doSomething();

singleton일 경우
SingletonClass.getInstance().doSomething();

하지만 싱글톤을 쓰게 되면, extends와 implements가 가능합니다.

각종 캐쉬에 관련된 Singleton을 잔뜩 만들었다고 칩시다. 사용자 정보 캐쉬, 통장정보 캐쉬 등등등. 이렇게 여러놈 필요한데, 대동소이할 겁니다. 이럴 때 상속을 쓰지요. Singleton을 쓰면, 상속이나 생성자 등을 활용할 수가 있습니다.
Singleton "을" 상속하는 것은 안 될 생각이지만 Singleton "이" 상속 받는 것은 때때로 괜찮은 생각입니다.

2. enum을 이용한 Singleton

public enum Singleton{
    INSTANCE;
}

와 같은 방식으로 enum을 이용한 Singleton을 쓸 수 있습니다. Singleton 생성 방법 여러 개 중에서 static member를 만들어서 쓰는 방법과 유사하다고 보면 됩니다.
주의할 점은 Serialize에 관련된 부분입니다. enum을 쓰게 되면 Serialize를 자동으로 구현하도록 되기 때문에 객체를 Serialize하는 게 가능합니다. 그러나 enum의 Serialize는 우리 생각과 좀 다르게 움직입니다.

다음은 name이라는 멤버 변수와 그에 대한 getter,setter를 가진 Singleton을 enum으로 만든 예제입니다.

public enum EnumSerialize implements Serializable{
 INST;
 private String name = "old";
 
 public String getName() {
     return name;
    }

    public void setName(String name) {
     this.name = name;
    }

 public static void main(String[] arg) throws FileNotFoundException, IOException, ClassNotFoundException{
  String file = "F:\\enum-serialize";
  //쓰기 부분
  /*
  EnumSerialize obj = EnumSerialize.INST;
  obj.setName("new");
  ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
  oos.writeObject(obj);
  oos.close();
  //*/
  
  //읽기부분
  /*
  ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
  EnumSerialize obj2 = (EnumSerialize)ois.readObject();
  System.out.println(obj2.getName());
  //*/
 }
}

예제 코드를 한 줄이라도 줄이고자 close 안 했습니다. 실제 프로그램 짤때는 꼭하세요!

main에서 파일 경로를 적당히 고치고 위쪽 주석을 풀어주면 Serialize해서 파일에 박습니다. 박기전에 우리는 name을 "new"라는 String으로 만들었습니다. 그리고 위쪽을 다시 막고 아래쪽을 풀어주면 읽기부분이 실행됩니다.
그러면 읽어들인 객체의 name은 "new"일 거라고 기대하겠지만, "old"가 출력됩니다.(위와 아래를 동시에 풀면 new가 출력됩니다.) 멤버 변수가 serialize가 안 됩니다.

이유는 이러합니다. deserialize를 하기 위해서는 먼저 classloader에 의해 EnumSerialize라는 클래스가 로드됩니다.(내부적으로 enum은 역시 classloader에 의해 로드되는 클래스입니다. ) 그러면서 static 멤버 변수가 로드됩니다. 이때 INST 라는 Singleton instance가 로드가 됩니다. 이 녀석은 name을 "old"로 알고 있지요. 그리고 나서야 deserialize가 이루어지는데, deserialize를 해보니 INST라는 놈이라는 것을 알게 됩니다. 그리고는 이미 로드된 INST를 리턴해버립니다. 그렇게 하지 않으면, 여러 개의 enum 클래스가 로드될 수 있기 때문에 1개의 enum을 로드하기 위한 조치입니다.

3. 인자가 있는 Singleton

Singleton에 이런 저런 세팅을 할 필요가 있을 때가 있습니다. 그러나 setter를 마구 열어 놓는 것은 또 좋지 않은 생각 같습니다. 다음과 같은 몇 가지 방안들이 있습니다.

3-1. static member 변수

설정 파일을 읽어서 singleton을 생성하는 경우에 유용합니다. 생성자 안에서 그 파일을 읽으면 됩니다.

public enum Config{
    INST;
    private static String path = "경로";
    Config(){
          //path 변수를 이용해서 각종 멤머 변수 설정!
    }
}

경로가 바뀔 수 있다거나 singleton 로드 후에 값이 세팅되어야 할 경우에는 쓸 수 없습니다만, 일반적으로는 가장 괜찮은 방법입니다.

3-2. default setter 제공

public enum Config{
    INST;
    private String member;
    public String getMember(){return this.member;}
    void setMember(String arg){ this.member = arg;}
}

setter는 default 이고, getter는 public 인게 포인트입니다.
이렇게 만들고, 저 Singleton이 있는 패키지에서만 setting을 해줍니다. 여기서 주의할 점은 다중 쓰레드 등에서는 setter와 getter가 동시에 호출되거나 하는 상황의 경우 위험할 수 있으니각종 Lock 을 생각을 해야 합니다. member하나에 ReadWriterLock 객체를 하나씩 만들어서 setter에서는 write lock을 잡고 getter에서는 read lock을 잡으면 됩니다. 코드는 무진장 길어지겠죠.

public enum Config {
    INST;
    private String member;
    private ReadWriteLock mrwl = new ReentrantReadWriteLock();
    public String getMember() {
     Lock lock = mrwl.readLock();
     try{
      lock.lock();
         return member;
     }finally{
      lock.unlock();
     }
    }
    void setMember(String member) {
      Lock lock = mrwl.writeLock();
      try{
       lock.lock();
         this.member = member;
      }finally{
       lock.unlock();
      }
    }
}

꼴랑 멤버 1개 짜리가 이렇게 길어질 수도 있습니다.


3-3 reflection 활용

정말정말 비추하는 방법이지만 어쩔 수 없는 사태에서는 이것도 방법입니다. reflection을 쓰면 private이고 뭐고 무시하고 접근할 수 있기 때문에 어쩔 수 없으면 이 방법도 고려해볼만 합니다. 특히 기존에 딴 사람이 짜논 것을 수정해야 하는 사태에서는 쓰게 되는 경우가 종종 있습니다.
여기서도 주의하셔야 할 것이 다중 쓰레드 환경에서는 lock 없이 되기 어렵습니다. getter 코드에서 lock이 안 잡혀 있는 것은 불보듯 뻔한 일이고, 그러면 충돌을 감수하는 수 밖에 없습니다. 백에 하나 정도 에러는 용서할 수 있다면 lock 무시하고 reflection으로 가는 것도 ....
비추하는 바이므로 예제코드는 생략합니다. reflection은 각종 검색 등을 통해 손쉽게 정보를 구할 수 있을 겁니다. 

by 삼실청년 | 2011/02/02 23:41 | 컴터질~ | 트랙백 | 덧글(2)

트랙백 주소 : http://iilii.egloos.com/tb/5433527
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by 규이 at 2011/12/09 10:20
안녕하세요~
싱글톤 관련된 내용 잘 보고 갑니다.
한가지 궁금한게 있는데 괜찮으실까요?

classloader에 의해 클래스가 로딩되는거는 어떻게 하면 알수가 있나요?
Commented by 삼실청년 at 2011/12/28 23:56
-verbose 옵션을 줘서 실행시키면 로드되는 클래스 정보가 뜹니다.

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶