싱글톤 패턴(2) - 싱글톤 패턴을 깨뜨리는 방법, 안전하고 단순하게 구현하는 방법
1) 싱글톤 패턴을 깨뜨리는 방법
- 자바에서는 싱글톤 패턴을 깨뜨리는 2가지 방법이 있습니다.
첫째는, 리플렉션 사용하기
둘째는, 직렬화 & 역직렬화 사용하기 입니다.
(1) 리플렉션 사용하기
- 자바에서는 리플렉션을 사용해서 싱글톤 패턴을 깨뜨릴 수 있습니다.
public class App{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException {
Settings settings = Settings.getInstance();
Constructor<Settings> constructor = Settings.class.getDeclaredConstructor();
constructor.setAccessible(true);
Settings settings1 = constructor.newInstance();
System.out.println(settings == settings1);
}
}
// 실행 결과
false
(2) 직렬화 & 역직렬화 사용하기
- 자바에서는 객체를 파일 형태로 저장(=직렬화)했다가, 읽어 들이는(=역직렬화)것이 가능합니다.
직렬화 및 역직렬화가 가능하려면, 해당 클래스에 Serializable 인터페이스를 구현해야 합니다.
역직렬화를 할 때는, 생성자로 객체를 만들어주므로, 다른 객체가 만들어집니다.
public class Settings implements Serializable{
private static volatile Settings instance;
private Settings(){}
private static class SettingsHolder{
private static final Settings INSTANCE = new Settings();
}
public static Settings getInstance(){
return SettingsHolder.INSTANCE;
}
}
public class App {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException {
Settings settings = Settings.getInstance();
try(ObjectOutput out = new ObjectOutputStream(new FileOutputStream("settings.obj"))){
out.writeObject(settings);
}
try(ObjectInput in = new ObjectInputStream(new FileInputStream("settings.obj"))){
settings1 = (Settings)in.readObject();
}
System.out.println(settings == settings1);
}
}
// 실행 결과
false
- 역직렬화 같은 경우는, readResolve라는 시그니처를 갖고 있으면, 역직렬화를 할 때 사용합니다.
readResolve메소드 안에 getInstace 메소드를 넣어두면, 결과적으로 getInstance 메소드를 사용합니다.
이렇게 하면 동일한 객체를 얻을 수 있습니다.
반면, 리플렉션은 이러한 대응이 불가능합니다.
public class Settings implements Serializable{
private static volatile Settings instance;
private Settings(){}
private static class SettingsHolder{
private static final Settings INSTANCE = new Settings();
}
public static Settings getInstance(){
return SettingsHolder.INSTANCE;
}
protected Object readResolve(){
return getInstance();
}
}
2) 안전하고 단순하게 구현하는 방법
- 자바가 제공하는 enum을 사용해서 싱글톤 패턴을 구현할 수도 있습니다.
enum을 사용하면 리플렉션에 안전한 코드가 됩니다.
enum 클래스는 리플렉션에서 newInstance를 할 수 없도록 막아놨기 때문입니다.
반면, 인스턴스를 미리 만들어야 하고, 상속을 사용하지 못한다는 단점이 있습니다.
public enum Settings{
INSTANCE;
private Settings(){
}
private Integer number;
public Integer getNumber(){
return number;
}
public void setNumber(Integer number){
this.number = number;
}
}
- 또한, enum은 기본적으로 Serializable 인터페이스를 구현하며, 직렬화 & 역직렬화에 안전합니다.
참고
백기선 코딩으로 학습하는 GoF의 디자인 패턴