본문 바로가기

디자인패턴

[디자인 패턴] 싱글톤 패턴(Singleton Pattern)

정의

특정 클래스의 인스턴스가 하나만 만들어지고, 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴

Singleton Pattern UML

  1. singleton private 멤버변수 선언 
  2. 생성자를 private로 선언하여 외부에 노출이 되지 않도록 함
  3. 이후에 Static으로 전역에서 접근이 가능한 메소드(getInstance)를 생성해서 인스턴스를 반환 => 멤버변수에 이미 변수가 생성이 되어 있다면 해당 인스턴스를 리턴하며, 만약 인스턴스가 한번도 초기화 되지 않았다면, 생성한 후 리턴

 

 

예시) 프린터를 사용하는 사람이 여러명이라고 치자. 프린터를 사용하는 사람이 많다고 해서 프린터를 여러대를 구매하여 사용하는것은 매우 불필요한 낭비를 발생. => 해결책 프린터를 하나만 구매하여 공유하는 방법

 

프린터 관리자
public class Printer {
  // 외부에 제공할 자기 자신의 인스턴스
  private static Printer printer = null;
  private Printer() { }
  // 자기 자신의 인스턴스를 외부에 제공
  public static Printer getPrinter(){
    if (printer == null) {
      // Printer 인스턴스 생성
      printer = new Printer();
    }
    return printer;
  }
  public void print(String str) {
    System.out.println(str);
  }
}

 

프린터 사용자
public class User {
  private String name;
  public User(String name) { this.name = name; }
  public void print() {
    Printer printer = printer.getPrinter();
    printer.print(this.name + " print using " + printer.toString());
  }
}

public class Client {
  private static final int USER_NUM = 5;
  public static void main(String[] args) {
    User[] user = new User[USER_NUM];
    for (int i = 0; i < USER_NUM; i++) {
      // User 인스턴스 생성
      user[i] = new User((i+1))
      user[i].print();
    }
  }
}

 

장점

- 다른 모든 클래스에서 접근 할 수 있다.

- 인스턴스가 하나만 생성됨이 보장된다.

- Lazy initialization(게으르게 생성) 하여 구현 될 수 있다.

 

게으르게 생성할 수 있다는 것은, 프로그램 실행 중에 인스턴스를 생성 할 수 있다는 것이다.

이는 Singleton 패턴을 적용하지 않고 전역변수를 사용 했을때에 비해서 장점인데, 전역변수를 사용하면 프로그램 실행시에 인스턴스를 바로 생성해야한다.

이렇게 할 때 문제점은, 생성된 인스턴스가 프로그램이 실행 되는 동안 한번도 사용되지 않을 경우 자원이 낭비된다.

그렇기 때문에 게으르게 생성되는 것이 Singleton의 장점이라고 할 수 있다.

단점

- Singleton 클래스에 대한 의존도가 높아진다.

- 멀티 스레드 적용 시 동기화 문제가 생길 수 있다.

 

싱글톤 패턴을 적용하면 여러 클래스나 컴포넌트가 싱글톤 클래스에 의존하게 된다. 이는 시스템의 Coupling(결합도)를 높이는 결과를 가져오기 때문에 주의해야한다.

 

멀티 스레드를 적용하면 여러 스레드에서 한개의 싱글톤 클래스에 접근하게 된다. 이는 동기화 문제를 발생시킬 수 있으니 volatile 키워드를 통해 Double Checking Locking을 적용하거나 getInstance() 메소드에 synchronized 키워드를 적용하여 동기화 해야한다.

그런데 synchronized 키워드를 적용하면 스레드에서 getInstance() 할 떄마다 속도가 느려지는 문제가 발생한다.

 

 

멀티스레드에서의 싱글톤

정적변수에 인스턴스를 만들어 바로 초기화 하는 방법
public class Printer {
   // static 변수에 외부에 제공할 자기 자신의 인스턴스를 만들어 초기화
   private static Printer printer = new Printer();
   private Printer() { }
   // 자기 자신의 인스턴스를 외부에 제공
   public static Printer getPrinter(){
     return printer;
   }
   public void print(String str) {
     System.out.println(str);
   }
}

 

인스턴스를 만드는 메서드에 동기화하는 방법
public class Printer {
   // 외부에 제공할 자기 자신의 인스턴스
   private static Printer printer = null;
   private int counter = 0;
   private Printer() { }
   // 인스턴스를 만드는 메서드 동기화 (임계 구역)
   public synchronized static Printer getPrinter(){
     if (printer == null) {
       printer = new Printer(); // Printer 인스턴스 생성
     }
     return printer;
   }
   public void print(String str) {
     // 오직 하나의 스레드만 접근을 허용함 (임계 구역)
     // 성능을 위해 필요한 부분만을 임계 구역으로 설정한다.
     synchronized(this) {
       counter++;
       System.out.println(str + counter);
     }
   }
}

 

 

 

 

참조

https://ryusae.tistory.com/6

 

[디자인패턴] 싱글톤(Singleton) 패턴

1. 싱글톤 (Singleton) 이란?  Ensure a class has only one instance and provide a global point of access to it. - 해당 클래스의 인스턴스가 하나만 생성이 되는것을 보장하고, 어디서든지 그 인스턴스에..

ryusae.tistory.com

https://gmlwjd9405.github.io/2018/07/06/singleton-pattern.html

 

[Design Pattern] 싱글턴 패턴이란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io