싱글톤 패턴

SeokHwan An·2022년 8월 16일
0

디자인패턴

목록 보기
2/3

✏️싱글톤 패턴이란?

싱글톤 패턴은 객체의 인스턴스를 오직 1개만 생성되는 디자인 패턴으로 생성자가 여러번 호출되어도, 실제로는 생성되는 객체는 하나이며 최초로 생성된 이후에는 이미 생성된 객체를 반환하도록 만드는 것입니다.

왜 싱글톤 패턴을 사용하는가?

개발자들은 기존에 상황에서 왜 싱글톤 패턴이라는 디자인 패턴이 등장하게 되었는지 궁금할 것입니다. 일단 프로젝트가 점점 커지게 되면은 속도 역시 중요하지만 메모리 영역 역시 중요한 부분입니다. 서비스가 커지게 되면 사용자가 늘어나는데 같은 사용자들이 특정 기능을 이용할 때 객체가 계속 생성되는 것은 메모리 영역에 큰 치명적인 영향을 미치게 될 것입니다. 그렇기에 메모리 낭비를 방지하기 위해 싱글톤 패턴을 사용합니다.

싱글톤의 장점

싱글톤의 경우 메모리 낭비를 방지하는 것 이외에도 다른 클래스들이 데이터를 공유하는 것이 쉬으며 인스턴스가 한 개만 존재하는 것을 보증해줍니다. 한 번 인스턴스가 생성되면 그 이후로는 불러와서 사용하므로 객체 로딩 시간이 줄어 프로젝트의 성능이 좋아진다는 장점이 있습니다.

싱글톤 패턴의 단점

싱글톤의 경우 문제점이자 단점이 존재하는데 싱글톤 인스턴스가 너무 많은 책임을 가지고 있다면 다른 클래스의 인스턴스들 간의 결합도가 높아져 "개방-폐쇄 원칙"을 위배하게 됩니다. 또한 멀티쓰레드 환경에서 동기화처리가 되어있지 않다면 인스턴스가 두개가 생성되는 경우가 발생할 수 있습니다.

싱글톤 구현 방법

싱글톤을 구현하는 방식은 여러가지가 있지만 각각의 공통된 특징이 있습니다.

  • private 생성자만을 정의해 외부 클래스로부터 인스턴스 생성을 차단한다는 점입니다.
  • 싱글톤을 구현하고자 하는 클래스 내부에 멤버 변수로써 private static 객체 변수를 활용합니다.
  • public static 메소드를 활용해 외부에서 싱글톤 인스턴스에 접글할 수 있게 해주는 것입니다.

이어서 싱글톤을 구현하는 방식에 대해 알아보겠습니다.

📕1. Eager Initialization

위의 방식은 클래스의 로딩 단계에서 인스턴스를 생성해주는 방식입니다. 이는 사용하지 않을 경우에 낭비로 이루어질 수 있습니다.

public class Singleton {
    
    private static final Singleton instance = new Singleton();
    
    // private constructor to avoid client applications to use constructor
    private Singleton(){}
 
    public static Singleton getInstance(){
        return instance;
    }
}

📗2. Static Block Initilization

이는 위의 방식에서 static block을 통해 Exception Handling에 대한 옵션을 제공해줍니다. 하지만 이 방식 역시 클래스의 로딩 단계에서 인스턴스를 생성해줍니다.

public class Singleton {
 
    private static Singleton instance;
    
    private Singleton(){}
    
    //static block initialization for exception handling
    static{
        try{
            instance = new Singleton();
        }catch(Exception e){
            throw new RuntimeException("Exception occured in creating singleton instance");
        }
    }
    
    public static Singleton getInstance(){
        return instance;
    }
}

📘3. Lazy Initialization

이는 위의 두 방식과 달리 호출을 통해 인스턴스를 생성하는 방식입니다. 하지만 이는 치명적인 단점이 있는데 동시에 둘 이상의 사용자가 호출을 할 경우 인스턴스가 하나가 보장되지 않은 상황이 올 수 있습니다. 즉, multi-thread 환경에서 동기화 문제가 발생할 수 있습니다.

public class Singleton {
 
    private static Singleton instance;
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

📙4. Thread Safe Singlethon

이 방식은 3번의 문제점인 multi-thread 환경에서 동기화 문제를 해결해주는 방식으로 호출 메서드에 synchronized를 걸어주는 방식입니다. 이는 임계 영역을 형성해 해당 영역에서 오직 하나의 쓰레드만 접근 가능하게 해줍니다. 하지만 synchronized 키워드 자체에 대한 비용이 크기 때문에 싱클톤 인스턴스 오출이 잦은 경우 프로젝트의 성능이 떨어지게 됩니다.

public class Singleton {
 
    private static Singleton instance;
    
    private Singleton(){}
    
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
    
}

📚5. Bill Pugh Singleton Implementation

이는 현재 가장 널리 쓰이는 싱글톤 구현 방식으로 위의 발생하는 문제점들을 대부분 해결한 방식입니다. Inner class를 두어 싱글톤 인스턴스를 가지게 하는 방식으로 클래스가 로딩 되는 단계에서 생성되는 것이 아니라 호출이 이루어질 때 비로소 JVM 메모리에 로딩되어 인스턴스가 생성됩니다.

public class Singleton {
 
    private Singleton(){}
    
    private static class SingletonHelper{
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHelper.INSTANCE;
    }
}

다음과 같이 다양한 방식으로 싱글톤을 구현하는 방법에 대해 알아보았습니다.
모든 방식의 장단점이 있지만 현재 5번 방식이 가장 최선의 방법이라고 생각되며 싱글톤을 구성할 때에는 너무 많은 책임을 가지게 하지 않는 것이 중요합니다.

0개의 댓글