싱글턴 패턴은 애플리케이션 내에서 단 하나의 인스턴스만 생성되도록 하는 디자인 패턴입니다. 이 글에서는 싱글턴 패턴의 구현 방법과 이를 파괴할 수 있는 다양한 방법에 대해 다룹니다.
먼저, 가장 기본적인 싱글턴 패턴의 구현을 살펴보겠습니다.
import java.io.Serializable;
import java.lang.reflect.Constructor;
public class UniqueInstance implements Cloneable, Serializable {
private static volatile UniqueInstance instance;
private UniqueInstance() {
if (instance != null) {
throw new RuntimeException("이미 인스턴스가 생성되었습니다.");
}
}
public static UniqueInstance getInstance() {
if (instance == null) {
synchronized (UniqueInstance.class) {
if (instance == null) {
instance = new UniqueInstance();
}
}
}
return instance;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
private Object readResolve() {
return getInstance();
}
}
위 코드는 싱글턴 패턴의 인스턴스를 생성하고, 복제와 직렬화를 통한 인스턴스 생성을 막아줍니다.
다음은 싱글턴 패턴을 파괴하는 방법들을 테스트하는 코드들입니다.
클론을 이용한 파괴 시도
public class CloneTest {
public static void main(String[] args) {
try {
UniqueInstance uniqueInstance1 = UniqueInstance.getInstance();
UniqueInstance uniqueInstance2 = (UniqueInstance) uniqueInstance1.clone();
System.out.println(uniqueInstance1.hashCode());
System.out.println(uniqueInstance2.hashCode());
System.out.println(uniqueInstance1 == uniqueInstance2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
직렬화와 역직렬화를 이용한 파괴 시도
import java.io.*;
public class SerializationTest {
public static void main(String[] args) {
try {
UniqueInstance uniqueInstance1 = UniqueInstance.getInstance();
FileOutputStream fos = new FileOutputStream("uniqueInstance.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(uniqueInstance1);
FileInputStream fis = new FileInputStream("uniqueInstance.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
UniqueInstance uniqueInstance2 = (UniqueInstance) ois.readObject();
System.out.println(uniqueInstance1.hashCode());
System.out.println(uniqueInstance2.hashCode());
System.out.println(uniqueInstance1 == uniqueInstance2);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
리플렉션을 이용한 파괴 시도
import java.lang.reflect.Constructor;
public class ReflectionTest {
public static void main(String[] args) {
try {
UniqueInstance uniqueInstance1 = UniqueInstance.getInstance();
Constructor<UniqueInstance> constructor = UniqueInstance.class.getDeclaredConstructor();
constructor.setAccessible(true);
UniqueInstance uniqueInstance2 = constructor.newInstance();
System.out.println(uniqueInstance1.hashCode());
System.out.println(uniqueInstance2.hashCode());
System.out.println(uniqueInstance1 == uniqueInstance2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
위 코드들은 각각 클론, 직렬화/역직렬화, 리플렉션을 통해 싱글턴 패턴을 파괴하려는 시도를 보여줍니다. 그러나 UniqueInstance 클래스의 구현으로 인해 이러한 시도들은 모두 실패하게 됩니다.