이번글에서는 디자인 패턴 중 하나인 싱글톤 패턴에 대해서 알아봅니다.
싱글톤 패턴은 객체 지향 소프트웨어에서 반복되는 문제를 해결하기 위해 잘 설명해진 GoF 디자인 패턴 중 하나입니다. 싱글톤 패턴은 클래스의 인스턴스화를 단일 인스턴스로 제한하는 패턴입니다.
싱글톤 패턴을 사용하여 인스턴스를 한 개로만 가져가면 아래와 같은 이점이 있습니다.
싱글톤 패턴만의 장점도 분명하지만 반대로 안티패턴이라고 불릴만큼 단점도 많이 있습니다.
public class Coin {
private static final int ADD_MORE_COIN = 10;
private int coin;
private static Coin instance = new Coin(); // eagerly loads the singleton
private Coin() {
// private to prevent anyone else from instantiating
}
public static Coin getInstance() {
return instance;
}
public int getCoin() {
return coin;
}
public void addMoreCoin() {
coin += ADD_MORE_COIN;
}
public void deductCoin() {
coin--;
}
}
object Coin {
private var coin: Int = 0
fun getCoin():Int {
return coin
}
fun addCoin() {
coin += 10
}
fun deductCoin() {
coin--
}
}
멀티 스레드에서 하나의 싱글톤 객체에 접근할 때 경쟁 상태(Race Condition)이 발생하여 여러개의 인스턴스가 생성될 수 있어 자바에서는 synchronized로 감싸서 지연 초기화를 더블체킹과함께 스레드 세이프하게 생성해야한다.
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
앞서 제시한 싱글톤을 생성한 방법은 두가지 문제점이 있다
enum EnumSingleton {
INSTANCE;
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// EnumSingleton singleton = EnumSingleton.INSTANCE;
Enum은 직렬화와 역직렬화가 가능하기 때문에 별도의 Serializable
을 구현하지 않아도 되고 리플렉션을 시도하려고 하면 NoSuchMethodException
이 발생하기 때문에 외부에서 싱글톤 인스턴스를 생성하는 것은 불가능하므로 완벽한 싱글톤 형태라고 불린다.
싱글톤은 이점이 있는 패턴이지만 앱 전역의 상태 관리를 하는데 있어서 안티패턴으로 간주된다. 이로 인해 싱글톤에 대한 잠재적인 종속성이 도입되어 실제 코드를 분석하기 위한 어려움과 크고 리팩토링에 대한 비용도 증가한다. 그리고 SOLID 원칙중 SRP, DIP, OCP를 위반 할 수 있고 이로인해 테스트하기 어렵다.