본문 바로가기

CS/DesignPattern

(DesignPattern) 싱글턴 패턴

https://jwdeveloper.tistory.com/29?category=823919

 

(Effective Java) 아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라

싱글턴이란? 인스턴스를 오직 하나만 생성할 수 있는 클래스를 뜻함. 장점 고정된 메모리 영역을 얻으면서 한번의 new로 인스턴스를 공유하기 때문에 메모리 낭비를 방지할 수 있다. 두 번째 사용부터는 객체 로..

jwdeveloper.tistory.com

싱글턴 패턴이란?

인스턴스를 오직 하나만 생성할 수 있는 클래스를 뜻함.

 

장점

  • 고정된 메모리 영역을 얻으면서 한 번의 new로 인스턴스를 공유하기 때문에 메모리 낭비를 방지할 수 있다.
  • 두 번째 사용부터는 객체 로딩 시간이 줄어들어 성능이 좋아진다.

단점 

  • 싱글턴 인스턴스가 너무 많은 일을 하면 인스턴스의 간의 결합도가 높아진다. (OCP의 원칙에 위배 )
  • 디버깅이 어려움이 있다.
  • 테스트 코드의 작성의 어려움이 있다.

실습

 

개발 중인 시스템에서 스피커에 접근할 수 있는 클래스를 만들어보자

version 01

public class SystemSpeaker {
    private static SystemSpeaker instance;
    private int volume;

    private SystemSpeaker() {

    }

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

    public int getVolume() {
        return volume;
   }

    public void setVolume(int volume) {
        this.volume = volume;
    }
}

version 02

public class SystemSpeaker {
    private final static SystemSpeaker INSTANCE = new SystemSpeaker();

    private SystemSpeaker() {}

    public SystemSpeaker getInstance() {

        return INSTANCE;
    }
}

 

생성자를 private으로 막아서 인스턴스가 생성되는 것을 막았는데 과연 이것이 안전한 방법일까?

 

답은 아니다! 왜냐면 리플렉션으로 해당 생성자의 접근을 가능하게 할 수 있기 때문이다.

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

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



        Class<SystemSpeaker> systemSpeakerClass = SystemSpeaker.class;
        Constructor<SystemSpeaker> declaredConstructor = systemSpeakerClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        SystemSpeaker instance3 = declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);
        System.out.println(instance3);
    }
}

실행 결과 세번째 리플렉션을 사용한 세번째 인스턴스의 주소값이 다르다는 것을 볼 수 있다.