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

Damsul·2023년 1월 25일
0

디자인패턴

목록 보기
12/15
post-thumbnail

한 개의 클래스에 오직 한 개의 인스턴스만 존재하도록 하고, 해당 클래스의 인스턴스 접근 시 항상 동일한 인스턴스를 반환하도록 하는 생성 패턴이다.

출처 : 위키디피아

  • Singleton : 하나의 인스턴스만을 생성하고, 항상 동일한 인스턴스 반환

예제

객체를 생성해주는 필드를 static final로 선언 후 객체를 생성하여 필드에 넣어놓고, 외부에서 해당 객체 생성시 기존에 생성한 객체를 반환해준다. 새로 생성하지 못하고 기존의 객체를 반환해 줄 수 있도록 생성자는 private으로 만들어야 한다.

  • Singleton

[UserService.java]

public class UserService {

    private static final UserService instance = new UserService();

    private UserService() {

    }
    
    public static UserService getInstance() {
        return instance;
    }
}
  • Client

[Client.java]

public class Client {

    public static void main(String[] args) {
        UserService user1 = UserService.getInstance();
        UserService user2 = UserService.getInstance();

        System.out.println(user1.equals(user2));
    }

}
  • 실행 결과

장단점

  • 장점
    • 메모리 절약이 가능하다.
      • 고정된 메모리 영역을 얻으면서 한번의 new로 인스턴스를 사용하기 때문
    • 객체 공유하기가 쉽다.
      • 싱글톤으로 만들어진 클래스의 인스턴스는 전역 인스턴스이기 때문
  • 단점
    • 동기화 처리를 해야한다.
      • 멀티쓰레드환경에서 인스턴스가 여러개 생성될 수 있기 때문
    • 상태 변경하지 않도록 통제 해야한다.
      • 하나의 객체를 공유하므로 상태를 변경해버리면 다른 클라이언트가 영향을 받을 수 있기 때문
    • 의존성이 높다.
      • 싱글톤 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유하기 때문
    • 유닛테스트하기 어렵다.
      • 각 테스트마다 '독립적인' 인스턴스를 만드기 어렵기 때문

싱글톤 패턴 종류

  1. 단순한 메서드 호출
  • 원자성이 결어되어 있음
  • 멀티 스레드 환경에서 인스턴스를 2개 이상 만들 수 있음.
public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

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

        return instance;
    }
}
  1. 동기화
  • 인스턴스 반환하기 전까지 잠금할 수 있음
  • getInstance() 메서드 호출할 때마다 잠글이 걸려 성능저하란 단점
public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

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

        return instance;
    }
}
  1. 정적 멤버/블럭
  • 클래스 로딩 때 인스턴스 생성
  • 불필요한 자원낭비
    • 인스턴스가 필요없는 경우에도 인스턴스 생성하기 때문
public class Singleton {

    private static Singleton instance = new Singleton(); // 정적 멤버

    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }
}
public class Singleton {

    private static Singleton instance = null;

    static { // 정적 블럭
        instance = new Singleton();
    }

    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }
}
  1. Lazy Holder(중첩 클래스)
  • 불필요한 자원낭비 없이 getInstance()가 호출될 때 Holder 클래스가 로딩되어 인스턴스를 생성한다.
public class Singleton {
   private static class SingletonInstanceHolder{
       private static final Singleton INSTANCE = new Singleton();
   }

    public static Singleton getInstance() {
        return SingletonInstanceHolder.INSTANCE;
    }
}
  1. 이중확인잠금(DCL)
  • Double Checked Locking, 잠금 전에 한번, 객체 생성 전에 한번 인스턴스 생성 여부를 체크하면 인스턴스가 존재할 때만 잠금을 걸 수 있기 때문에 앞서 생겼던 문제점을 해결할 수 있다.
public class Singleton {
    private volatile Singleton instance;

    private Singleton() {

    }

    public Singleton getInstance() {
        if (instance == null) { // 잠금 전에 한번
            synchronized (Singleton.class) {
                if (instance == null) { // 생성 전에 한번
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  1. enum
  • 기본적으로 스레드 세이프 보장
  • 멤버 변수화 메서드도 정의 가능
public enum Singleton {
    INSTANCE;

    private int value = 3;

    public int getValue() {
        return value;
    }
}

주로 4번과 6번이 사용된다.

profile
내 맘대로 작성하는 개발일지/ 작고 소중한 개발창고

0개의 댓글