ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [자바 디자인 패턴] 싱글톤 패턴(Singleton Pattern)
    Java/Design Pattern 2020. 6. 1. 22:44

    ■싱글톤 패턴 구현 방법 :: 정적 팩토리 메소드

    public class UserDao {
    private static UserDao INSTANCE;
    private UserDao(ConnectionMaker connectionMaker) {
    if(INSTANCE != null){
    throw new IllegalStateException();
    }
    this.connectionMaker = connectionMaker;
    }
    public static synchronized UserDao getlnstance() {
    if (INSTANCE == null) INSTANCE = new UserDao(???);
    return INSTANCE;
    }
    }

    먼저 클래스 밖에서는 객체를 생성하지 못하도록 생성자를 private으로 만든다

    생성자를 private으로 만들면 상속하지 못하기 때문에, 전체 시스템에서 


    해당 싱글톤 객체가 하나뿐이 보장된다. 


    그리고 리플렉션(AccessibleObject.setAccessible)을 사용한 private 생성자 호출을 방지하기 


    위해 INSTANCE가 생성된 적이 있었다면 RuntimeException을 호출한다.


    그리고 생성된 객체를 저장할 수 있는, 자신과 같은 타입의 private static 필드를 


    정의한다.


    다음으로 static factory 메서드getInstance()를 만들고, 이 메소드가 최초로 호출되는


    시점에서 한번만 객체가 생성되게 한다. 생성된 객체는 static 필드에 저장된다.


    싱글톤 객체가 한번 생성된 후에는, getInstance 메서드 호출을 통해 static 필드가 


    참조하는 싱클톤 객체를 얻을 수 있다.


    그리고 synchronized 키워드를 통해 멀티스레드 상황에서의 중복 객체 생성을 방지한다.


    이 방법의 장점은 API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다는 점이다.


    유일한 인스턴스를 반환하던 팩터리 메서드가 (예컨대) 호출하는 스레드별로 다른 


    인스턴스를 넘겨주게 할 수 있다.


    또한 정적 팩터리 메서드를 제네릭하게 바꿀 수 있다(item30)


    그리고 정적 팩터리 메서드 참조를 공급자(supplier)로 사용할 수 있다.


    예를들어 UserDao::getInstance를 Supplier<UserDao>로 사용하는 식이다(item43,item44)


    이런 장점들이 굳이 필요하지 않다면 아래 public final Instance 방식이 더 나을 것이다




    ■싱글톤 패턴 구현 방법 :: public final INSTANCE

    public class UserDao {
    public static final UserDao INSTANCE = new UserDao(???);
    private UserDao(ConnectionMaker connectionMaker) {
    if(INSTANCE != null){
    throw new IllegalStateException();
    }
    this.connectionMaker = connectionMaker;
    }
    }

    이 방법은 INSTANCE를 public으로 선언하여 클라이언트에서 인스턴스로 


    직접 참조하도록 한다. 그리고 INSTANCE를 final로 선언하여 딱 한번만 생성하게 한다.


    (정적 팩터리 메서드와 마찬가직로) 생성자를 private으로 생성하여 싱글톤 객체가 


    시스템 전체에서 하나만 존재함을 보장한다.


    또한 private 생성자 내에서 리플렉션을 사용한 생성자 호출을 방지하기 위해


    적절한 Exception을 호출한다.


    이 방법의 장점은 해당 클래스가 싱글턴임이 API에 명백히 드러난다는 점이다.


    public static 필드가 final이니 절대로 다른 객체를 참조할 수 없다.


    또한 간결하다는 점도 장점으로 들 수 있다.




    ■싱글톤 패턴의 문제점


    1. 싱글톤 패턴은 생성자를 private으로 제한하기 때문에 


    해당 싱글톤 객체를 상속해서 사용할 수 없다


    (이 부분은 장점으로 볼 수도 있겠다)


    2. 싱글톤 패턴은 객체를 생성하는 방식이 제한적이기 때문에


    테스트에서 사용될 때 mock 오브젝트 등으로 대체하기가 힘들다


    초기화 과정에서 생성자 등을 통해 사용할 오브젝트를 다이내믹하게 주입하기 


    힘들기 때문에, 필요한 오브젝트는 직접 오브젝트를 만들어 사용할 수 밖에 없다.


    또한 Mockito는 static method를 mocking하는 것을 허용하지 않아 PowerMock을 


    반드시 써야하는 제약이 생길 수도 있다.


    3. 서버에서 클래스 로더를 어떻게 구성하고 있느냐에 따라서 싱글톤 클래스임에도


    하나 이상의 객체가 만들어질 수 있다.


    여러 개의 JVM에 분산돼서 설치가 되는 경우에도 각각 독립적으로 객체가 


    생기기 때문에 싱글톤으로서의 가치가 떨어진다


    4. 싱근톤의 사용은 전역 상태를 만들 수 있다. 


    이는 객체지향 프로그래밍에선 권장되지 않는 프로그래밍 모델이다. 


    차라리 static 필드와 메소드로만 구성된 클래스를 사용하는 편이 나을 것.





    ■싱글톤 패턴과 멀티스레드


    싱글톤 패턴은 상태 관리에 주의를 기울여야 한다.


    왜냐하면 여러 스레드가 동시에 접근해서 사용할 수 있기 때문이다.


    멀티스레드에 대응하려면, stateless한 방식으로 싱글톤 객체를 디자인하는게 좋다.




    ■싱글톤 패턴 직렬화(Serializable)


    싱글톤 클래스를 직렬화하려면(item12) 단순히 Serializable을 구현한다고 선언하는


    것만으로는 부족하다. 모든 인스턴스 필드를 일시적(transient)이라고 선언하고 


    readResolve 메서드를 제공해야 한다(item89)

    private Object readResolve() {
    return INSTANCE;
    }

    이렇게 하지 않으면 직렬화된 인스턴스를 역직렬화할 때마다 새로운 인스턴스가


    만들어질수 있다!!

    'Java > Design Pattern' 카테고리의 다른 글

    [자바 디자인 패턴] Flyweight 패턴  (0) 2020.05.12

    댓글

Designed by Tistory.