싱글턴 패턴은 소프트웨어 디자인 패턴 중 하나로, 특정 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하는 패턴입니다. 이 패턴은 전역 변수를 사용하지 않고 객체를 전역적으로 액세스할 수 있게 해줍니다.

싱글턴 패턴의 목적 및 장점

싱글턴 패턴은 다음과 같은 목적과 장점이 있습니다.

  • 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;
    }
}