티스토리 뷰

1. 싱글턴 패턴이란?

해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 한다.

 

구성요소

1) Singleton

- getSingleData() : 다른 클라이언트들이 그 유일한 객체에 접근할 수 있도록 하는 인스턴스 함수를 제공한다.
- 유일 객체를 생성하는 역할을 자기 자신이 할 수 있도록 구현할 수도 있다.

 

2. 동기

장점:

1) 유일 객체에 대한 통제된 접근성

- Singleton 객체가 자신의 객체를 캡슐화하기 때문에, 클라이언트들이 언제, 어떻게 접근하는 지에 대해 매우 엄격한 통제를 한다.

 

2) 깔끔하게 쓸 수 있는 네임스페이스

- global variable에 대한 개선책이다. 네임스페이스에 global variable들이 마구잡이로 생성되고 삭제되는 것을 방지해준다.

- 자바 기준으로 static variable을 떠올리면 될 것 같다. 마구잡이로 전역변수를 사용하는 것에 비해 더 좋은 방법이다 정도로 생각.

 

 

3. 구현

1) 하나의 인스턴스만 생성되도록 한다.

이 방법도 총 3가지가 있다. 가장 간단한 방식은 다음과 같다.

public class Singleton {
    private static Singleton uniqueInstance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

lazy initialization도 하고, synchronized로 멀티스레드 환경에서도 싱글턴 객체의 역할을 하지만, 한 가지 치명적인 약점이 있다.

바로 객체 접근 함수 전체가 동기화 되었기 때문에 성능저하가 생긴다는 것. 동기화가 필요한 시점은 메소드가 처음 실행되는 부분 뿐이다.

 

그렇다면 성능을 개선시키기 위해 아예 클래스 로딩이 되는 시점에 객체를 생성해버리는 방법을 사용할 수 있다.

public class Singleton {
    private static Singleton uniqueInstance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return uniqueInstance;
    }
}

하지만 이 방법으로 잃는 것이 있는데, lazy loading을 더 이상하지 않기 때문에 필요하기도 전에 객체를 생성해버린다.

만약 Singleton 클래스가 매우 무겁다면, 문제를 야기할 수도 있다.

 

그렇다면 첫 번째 방법에서 객체가 처음 생성될 때만 타겟팅하는 방법이 있을까?
DCL(Double-Checking Logic)을 사용해서 동기화하는 부분을 줄일 수 있다. 객체를 생성하는 블록만 synchronized 시키는 것이다.

public class Singleton {
    private volatile static Singleton uniqueInstance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized(Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
    }
}

2) Subclassing

유일한 객체임을 보장하면서 특정 상황 싱글턴 클래스의 새로운 인스턴스 추가 없이 기능을 연장하고 싶을 때 이런 방식을 쓸 수 있다.

 

- 싱글턴 클래스를 베이스 클래스로 사용하는 것. (공통된 부분은 여기에)

- 베이스 싱글턴 클래스를 상속 받는 서브클래스를 만들고, 추가할 내용을 이곳에 구현한다.

- 싱글턴 객체는 서브 클래스 타입이지만, 접근 함수는 베이스 클래스나 서브 클래스에서 명시할 수 있다.

public abstract class SingletonBase {
    // Private static instance variable
    private static SingletonBase instance = null;

    // Protected constructor to allow subclass access
    protected SingletonBase() {
        // Initialize the instance if it doesn't exist
        if (instance == null) {
            instance = this;
        }
    }

    // Static method to get the Singleton instance
    public static SingletonBase getInstance() {
        if (instance == null) {
            instance = new SubclassOfSingleton(); // Create a default instance if needed
        }
        return instance;
    }

    // Common behavior/methods
    public abstract void commonMethod();
}

public class SubclassOfSingleton extends SingletonBase {
    // Additional fields or methods specific to the subclass
    private String subclassField;

    public SubclassOfSingleton() {
        subclassField = "Initialized in Subclass";
    }

    @Override
    public void commonMethod() {
        System.out.println("Subclass's implementation of commonMethod");
    }

    // Additional methods specific to the subclass
    public String getSubclassField() {
        return subclassField;
    }
}

 

 

4. 개인적인 포인트

- registry of singletons 더 봐야될 거 같다.