[디자인패턴] 싱글턴 패턴 (Singleton)
April 20, 2024
싱글턴 패턴은 소프트웨어 디자인 패턴 중 하나로, 특정 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하는 패턴입니다. 이 패턴은 전역 변수를 사용하지 않고 객체를 전역적으로 액세스할 수 있게 해줍니다.
싱글턴 패턴의 목적 및 장점
싱글턴 패턴은 다음과 같은 목적과 장점이 있습니다.
- 1개의 객체 생성 보장: 싱글턴 패턴을 사용하면, 클래스의 인스턴스가 하나만 생성되는 것을 보장할 수 있음
- 전역 접근: 싱글턴 패턴을 사용하면, 객체를 인자로 전달하지 않아도 어디서든지 접근할 수 있음.
- 객체 생성 지연 : 싱글턴 객체는 필요할 때 생성됨.
- 데이터 공유 : 1개의 객체에서 모든 작업이 처리되기 때문에, 자원을 공유할 수 있음
시스템 디자인 측면에서 주의할 점
싱글턴 패턴이 디자인에 미치는 단점으로, 아래와 같은 내용을 주의해야 합니다.
- 유연성 저하: 싱글턴 패턴은 OOP의 원칙 중 하나인 “개방/폐쇄 원칙”을 위반할 수 있습니다. 확장이 어렵고 변경이 필요할 때 다른 클래스들에 영향을 줄 수 있음
- 멀티스레드 환경에서의 동기화 문제: 멀티스레드 환경에서는 객체 생성 과정에서 동시성을 관리해야 함. 두개의 스레드가 동시에 생성 요청 시, 두개의 객체가 생성될 수 있음.
- 테스트 어려움: 전역 객체의 특성을 가진 싱글턴은 테스트가 어렵고, Unit test가 어려울 수 있습니다.
싱글턴 코드 예제
아래 코드는 Client 클래스가 Singleton 클래스의 객체를 생성하고 사용하는 방법을 보여줍니다.
Client의 useSingleton()
메서드에서 Singleton.getInstance()
를 호출하여 Singleton 인스턴스를 얻고 사용합니다. 다른 곳에서도 getInstance()
를 호출하면, 이전에 생성된 객체가 전달되며, 동일한 객체를 사용할 수 있습니다.
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void doSomething() {
// do something
}
}
public class Client {
public void useSingleton() {
// 싱글턴 객체를 가져옴
Singleton singleton = Singleton.getInstance();
// singleton 객체를 사용할 수 있음.
singleton.doSomething();
}
}
위 코드를 클래스 다이어그램으로 그려보면 다음과 같습니다.
동시성 처리
만약 멀티 스레드 환경에서 사용하려면 동시성 처리를 해야 합니다. 처리를 하지 않으면, 여러 쓰레드에서 동시에 객체를 생성하여 1개 이상의 객체가 생성될 수 있습니다. 타이밍 문제로, 데이터를 공유하지 않는 문제 등이 발생할 수 있습니다.
따라서 멀티 스레드 환경에서 싱글턴을 사용하시면, 객체를 생성할 때 아래와 같이 synchronized
로 하나의 스레드만 생성에 참여할 수 있도록 하시면 됩니다.
이렇게 하시면 두개 이상의 객체가 만들어지는 일이 발생하지 않게 됩니다.
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}