본문 바로가기
IT/Teckweek

싱글톤 패턴 (Singleton Pattern)

by YEON-DU 2020. 10. 26.
반응형

출처 : Let’s Learn Singleton Design Pattern

 

이전에 게임 개발을 하면서, 싱글톤 패턴을 사용하여 개발한 적이 있다. 기본 Base Class를 모든 객체들이 갖고있고, 그 객체로 전체 게임을 컨트롤 할 수 있게끔 만들었는데 (Game Controller로 사용함) 막연하게 하나의 객체를 모든 객체들이 갖고있다. 라는 점만 무의식 중에 알고있어서, 명확한 정의를 알지 못해서 면접 준비를 하면서 정리된 말로 답할 수 없던 개념중에 하나이다. 아무튼 그렇다보니 한 번쯤 짚고 넘어갈 필요가 있어보여서 정리해본다. 싱글톤 패턴!

 

싱글톤 패턴이란?

하나의 객체를 생성하고, 생성된 객체를 어디서든 참조할 수 있도록 하는 패턴.

여기서 주의할 점은 클래스를 static(전역 변수)으로 선언하는 것이 아닌, 동적으로 생성하고 heap 영역에서 GC로 관리하도록 하되, 단 하나의 객체를 생성하는 것이다.

싱글톤 패턴을 따르는 클래스는, 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고, 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다.

 

싱글톤 패턴의 장점

고정된 메모리 영역을 얻으면서 한 번의 동적 할당으로 인스턴스를 사용하여 메모리 낭비를 방지함.

싱글톤으로 만들어진 객체를 사용하여 다른 객체 간 데이터를 공유하기 쉬움.

주로 공통된 객체를 여러 개 생성하는 DBCP(데이터베이스 커넥션 풀) 같은 곳에서 사용.

 

싱글톤 패턴의 단점

싱글 객체가 너무 많은 일을 하거나, 많은 데이터를 공유시킬 경우 다른 객체간의 결합도가 높아져 *개방-폐쇄의 원칙을 위배할 수 있다. (단위 테스트를 어렵게 만들고, 숨겨진 종속성을 만들게 된다)

멀티스레드 환경에서 동기화 처리를 제대로 해주지 않으면 싱글 객체가 2개 이상 생성될 수 있다.

(따라서 멀티스레드에서 안전한 Thread-safe한 싱글 클래스를 만들어야 한다)

 

*개방-폐쇄의 원칙OCP(Open-Closed Principle) : 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해 열려있고, 수정에 대해서는 닫혀있어야 한다. 

 

싱글톤 객체 만드는 방법 (JAVA Ver.)

1. 이른 초기화Eager initialization : 싱글 객체를 만드는 가장 간단한 방법

public class Singleton  
{ 
  // public instance initialized when loading the class 
  private static final GFG instance = new Singleton(); 
  
  private Singleton() 
  { 
    // private constructor 
  } 
  public static Singleton getInstance(){ 
        return instance; 
    } 
} 

 

구현하기 쉽지만 리소스 및 CPU 오버헤드를 유발할 수 있다. 또한 예외처리가 불가능하다. 클래스 초기화 비용이 리소스 측면에서 적거나 프로그램에 항상 클래스 인스턴스가 필요한 경우 사용한다.

 

2. 정적 블록 사용Using static block

// Java code to create singleton class 
// Using Static block 
public class Singleton  
{ 
  // public instance 
  public static Singleton instance; 
  
  private Singleton()  
  { 
    // private constructor 
  } 
static
  { 
    // static block to initialize instance 
    instance = new Singleton(); 
  } 
} 

 

이른 초기화 방식에서 예외처리를 추가하고자 할 때 사용하는 방식이다. 이른 초기화 방식과의 차이점은 정적 블록에 생성되기 때문에 생성과 동시에 접근할 수 있다는 점이다. 예외의 경우 정적 블록에서 처리를 할 수 있다. 단점은 이른 초기화와 마찬가지로 리소스 및 CPU 오버헤드를 유발할 수 있다.

 

3. 느린 초기화Lazy initialization

//Java Code to create singleton class 
// With Lazy initialization 
public class Singleton  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static Singleton instance; 
  
  private Singleton()  
  { 
    // private constructor 
  } 
  
  //method to return instance of class 
  public static Singleton getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new Singleton(); 
    } 
    return instance; 
  } 
} 

 

필요한 경우에만 객체가 생성된다. (메모리 누수 방지) 따라서 이른 초기화 방식의 단점을 보완할 수 있다. (메소드 상에서 예외처리도 가능하다) 그러나 멀티스레드 환경에서 객체를 2번 생성할 여지가 있다. 또한 매번 null조건을 확인해야 하며, 인스턴스에 직접 액세스할 수 없다는 단점이 있다.

 

4. Thread Safe Singleton

// Java program to create Thread Safe 
// Singleton class 
public class Singleton  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static Singleton instance; 
  
  private Singleton()  
  { 
    // private constructor 
  } 
  
 //synchronized method to control simultaneous access 
  synchronized public static Singleton getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new Singleton(); 
    } 
    return instance; 
  } 
} 

 

멀티 스레드 환경에서 싱글 개체가 2개 이상 생성되지 않는, 싱글 속성이 유지되는 싱글 객체. 느린 초기화를 사용가능하고 멀티 스레드에서 안전하다. 단점으로는 getInstance()가 동기화 되기 때문에 여러 스레드가 동시에 액세스할 수 없어 성능이 저하된다.

 

5. 이중 검사 잠금을 사용한 느린 초기화 방식Lazy initialization with Double-checked locking

// Java code to explain double check locking 
public class Singleton  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static Singleton instance; 
  
  private Singleton()  
  { 
    // private constructor 
  } 
  
  public static Singleton getInstance() 
  { 
    if (instance == null)  
    { 
      //synchronized block to remove overhead 
      synchronized (Singleton.class) 
      { 
        if(instance == null) 
        { 
          // if instance is null, initialize 
          instance = new Singleton(); 
        } 
        
      } 
    } 
    return instance; 
  } 
} 

느린 초기화를 사용 가능하고, 스레드로부터 안전하다. synchronized 키워드를 사용하여 성능 오버 헤드가 감소한다. 단점으로는 성능에 영향을 받을 수 있다. 이중 검사 잠금을 사용하기 때문에 고성능 멀티 스레드 응용 프로그램에 사용하는 것이 좋다.

 

6. Bill Pugh Singleton

// Java code for Bill Pugh Singleton Implementaion 
public class Singleton  
{ 
  
  private Singleton()  
  { 
    // private constructor 
  } 
  
  // Inner class to provide instance of class 
  private static class BillPughSingleton 
  { 
    private static final Singleton INSTANCE = new Singleton(); 
  } 
  
  public static Singleton getInstance()  
  { 
    return BillPughSingleton.INSTANCE; 
  } 
} 

 

싱글톤 클래스가 로드되면, 내부 클래스는 로드 되지 않으므로 클래스를 로드할 때마다 객체를 생성하지 않는다. 내부 클래스는 getInstance()가 호출 될 때만 생성된다. 따라서 이른 초기화 방식처럼 보이지만 느린 초기화 방식으로 동작하게 된다. 이 방식은 동기화를 사용하지 않아서 가장 많이 사용되는 방식이라고 한다.

 

 

참고자료

www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/

jeong-pro.tistory.com/86

github.com/GimunLee/tech-refrigerator/blob/master/Design%20Pattern/Singleton%20Pattern.md#%EF%B8%8F-singleton-pattern

반응형

'IT > Teckweek' 카테고리의 다른 글

머신러닝과 딥러닝의 차이  (0) 2020.11.30
www.도메인.com 을 치면 일어나는 일  (1) 2020.11.22
얕은 복사와 깊은 복사  (0) 2020.10.19
최단 경로 문제  (0) 2020.10.12
객체지향 프로그래밍이란?  (0) 2020.10.05

댓글