반응형

싱글톤 패턴(Singleton Pattern)

  - 싱글톤 패턴은 생성패턴(Creational Pattern) 중 하나이다.
  - 싱글톤 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴이다.
  - 싱글톤 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현방법을 시스템과 분리해준다.

  - 하나의 객체만을 생성해 이후에 호출된 곳에서는 생성된 객체를 반환하여 프로그램 전반에서 하나의 인스턴스만을 사용하게 하는 패턴이다.

 

장점

  - 한번의 객체 생성으로 재사용이 가능하여 메모리 낭비를 방지한다.

  - 시스템 런타임, 환경 세팅에 대한 정보 등, 인스턴스가 여러개 일 때 생기는 문제를 미연에 방지할 수 있다.

 

문제점

  - 멀티 스레드 환경에서 안전하지 않다. (대처가 가능하다.)

  - 객체간의 결함도가 높아지고 변경에 유연하게 대처할 수 없다. 싱글톤 객체가 변경되면 이를 참조하고 있는 모든 값들이 변경되어야 한다.

  - 객체 상태를 유지하면 큰 문제가 발생한다.(stateful)

코드

  * 여러가지 싱글턴 구현 방법이있다.

  1. 기본 Singleton
  2. 이른 초기화(Eager Initialization)
  3. synchronized 사용
  4. static inner SIngleton(권장하는 방법)
  5. enum SIngleton(권장하는 방법)

 

1. 기본 Singleton

 - 단점이 Thread Safe 하지 못하다.

 

public class SingletonStatic {

    private static SingletonStatic instance;

    private SingletonStatic() {}

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

}

 

 

2. Eager Initialization

 - Thread Safe

 - 단점이 인스턴스를 미리 만들어 놓는다는 단점이 있다.

 

public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }

}

 

3. Thread Safe Singleton(synchronized 사용)

  - synchronized를 통해서 여러 쓰레드에서 동시에 접근하는 것을 막을 수 있다.(Thread Safe)

  - 동기화 처리하는 작업 때문에 성능에 불이익이 생길 수 있다.

 

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;
    
    private ThreadSafeSingleton() {}

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

 

4. static inner SIngleton(권장하는 방법)

  - SingletonHelper 클래스는 SIngleton 클래스가 Load 될 때에도 Load 되지 않다가 getInstance()를 호출할 때 jvm 메모리에 로드되어 인스턴스를 생성한다. synchronized를 사용하지 않기 때문에 synchronized 키워드 자체에 대한 성능 저하 문제를 해결할 수 있다.

  - 단점으로 자바 리플렉션을 이용하면 싱글톤 패턴이 깨진다.

 

public class BillPughSIngleton {

    private BillPughSIngleton(){}

    private static class SingletonHelper{
        private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
    }

    public static BillPughSIngleton getInstance(){
        return SingletonHelper.INSTANCE;
    }
    
}

 

객체 상태를 유지했을 경우 주의 (stateful)

BillPughSIngleton.java

 

public class BillPughSIngleton {

    private String apartment;

    private BillPughSIngleton(){}

    private static class SingletonHelper{
        private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
    }

    public static BillPughSIngleton getInstance(){
        return SingletonHelper.INSTANCE;
    }

    public String getApartment() {
        return apartment;
    }

    public void setApartment(String apartment) {
        this.apartment = apartment;
    }

}

 

SIngletonTest.java

 

    @Test
    void singleTonStatefulTest() {

        BillPughSIngleton instance1 = BillPughSIngleton.getInstance();
        BillPughSIngleton instance2 = BillPughSIngleton.getInstance();

        instance1.setApartment("아크로빌 아파트");
        instance2.setApartment("한남더힐 아파트");

        System.out.println(instance1.getApartment());
        System.out.println(instance2.getApartment());


    }

  - stateful하게 코드를 작성하게 되면 위와 같이 객체의 상태가 바뀌는 문제가 발생하게 된다.

무결성하게(stateless) 코드를 작성하여야 한다.

 

BillPughSIngleton.java

  - setApartment 값을 바로 return 한다.

 

public class BillPughSIngleton {

    private String apartment;

    private BillPughSIngleton(){}

    private static class SingletonHelper{
        private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
    }

    public static BillPughSIngleton getInstance(){
        return SingletonHelper.INSTANCE;
    }

    public String getApartment() {
        return apartment;
    }

    public String setApartment(String apartment) {
        this.apartment = apartment;
        return apartment;
    }

}

 

SIngletonTest.java

 

   @Test
    void singleTonStatefulTest() {

        BillPughSIngleton instance1 = BillPughSIngleton.getInstance();
        BillPughSIngleton instance2 = BillPughSIngleton.getInstance();

        String apartment1 = instance1.setApartment("아크로빌 아파트");
        String apartment2 = instance2.setApartment("한남더힐 아파트");

        System.out.println(apartment1);
        System.out.println(apartment2);
        
    }

 

5. enum Singleton(권장하는 방법)

 - 4번(static inner Singleton)과 같이 권장하는 방법으로 enum 클래스로 사용하는 방법이다.

 - 자바 리플랙션을 이용해도 enum 클래스는 newInstance를 이용해서 새로운 인스턴스를 생성할 수 없어서 더 안전하다.

 - 단점으로는 상속을 사용하지 못하고, 사용하지 않아도 미리 만들어 놓는다는 단점이 있다.  

 

public enum ThreadSafeV5 {

    INSTANCE;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 

실무 사용 사례

1. 자바 java.lang.Runtime 클래스가 싱글톤 패턴으로 구현되어 있다.

2. java Application 개발에서 많이 사용되는 스프링 프레임워크는 스프링 빈 컨테이너의 도움을 받아 싱글톤 스코프로 관리된다.

스프링 프레임워크를 사용하면 싱글톤 패턴의 문제점들을 보완하면서 장점을 누릴수 있다.  

3. 다른 디자인 패턴(빌더, 퍼사드, 추상 팩토리 등) 구현체의 일부로 쓰이기도 한다.

 

728x90
반응형

+ Recent posts