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

    2021. 3. 8.

    by. 웰시코더

    반응형

    -디자인패턴 중 싱글톤패턴에 대해 학습한다.


    싱글톤패턴이란?

     

     싱글톤패턴이란 객체를 하나만 생성하여, 이후 생성된 하나의 객체만을 참조하여 사용하는 패턴이다.

    new 연산자로 직접 인스턴스를 생성하지 못하게 하고, 인스턴스를 생성할 수 있는 static method를 만들어 이를 통해 인스턴스를 생성한다. 이 method를 여러번 사용하여도 매번 인스턴스가 새로 생성되어 heap 메모리에 올라가는 것이 아니라, 처음 호출시 단 한번만 메모리에 올라간 후 계속해서 그 인스턴스를 사용한다.

     

    예제 학습

    package Singleton;
    
    public class Singleton {
    	//static method를 통해 접근해야 하기 때문에 인스턴스를 담을 변수를 static 변수로 선언한다
    	private static Singleton singleton;
    	//인스턴스가 한 번 생성되었는지 체크하기 위한 변수
    	private int volume;
    	
    	//외부에서 인스턴스를 직접 생성하지 못하도록 생성자를 private로 만든다
    	private Singleton() {
    	
    	}
    	
    	//인스턴스를 생성할 수 있게 해주는 static method
    	public static Singleton getInstance() {
    		if(singleton == null) {
    			singleton = new Singleton();
    		}
    		
    		return singleton;
    	}
    	
    	//볼륨값 setter
    	public void setVolume(int volume) {
    		this.volume = volume;
    	}
    	
    	//볼륨값 getter
    	public int getVolume() {
    		return volume;
    	}
    }
    

     

     우선 생성자는 private로 생성하여 다른 객체들이 직접 인스턴스를 생성할 수 없도록 만든다.

     

     그리고 인스턴스를 할당할 변수(singleton)를 만드는데, static 변수로 만든다. 그 이유는 인스턴스를 생성할 수 있게 해주는 getInstance()가 static 변수이기 때문이다.

     이를 이해하기 위해 메모리에 대한 기본 개념을 알아야 한다. 일반적으로 static이 붙은 메소드,변수 등은 메모리의 Static 영역에 프로그램 종료까지 한 번만 할당되어 올라간다. new를 통해 인스턴스를 생성하면 Heap영역에 올라간다.

     그렇기 때문에 static method는 객체 생성 없이 Static 영역에 올라가 바로 호출이 가능하다. 

     

     그런데 여기서 만약 인스턴스를 할당할 변수(singleton)를 인스턴스 변수로 만든다면 static method를 호출할 때 변수가 메모리에 할당되어있지 않을 수 있기 때문에 문제가 발생할 수 있다. 그렇기 때문에 static method는 static 변수만 사용하도록 java에서 강제하고 있다. 아래 그림을 참고하기 바란다.

     

     

    메모리 static영역과 heap영역

     

     

    이제 위의 SIngleton class를 테스트해보자.

     

    package Singleton;
    
    public class Main {
    	public static void main(String[] args) {
    		
    		//getInstance()를 통해 인스턴스 생성(singleton1,singleton2는 같은 인스턴스를 참조)
    		Singleton singleton1 = Singleton.getInstance();
    		Singleton singleton2 = Singleton.getInstance();
    		
    		//볼륨 2 set
    		singleton1.setVolume(2);
    		
    		//singleton1,singleton2 모두 같은 인스턴스를 참조하기 때문에 그 인스턴스의 인스턴스 변수도 같은 값을 출력
    		System.out.println(singleton1.getVolume());
    		System.out.println(singleton2.getVolume());
    		
    		singleton1.setVolume(7);
    		
    		System.out.println(singleton1.getVolume());
    		System.out.println(singleton2.getVolume());
    		
    	}
    
    }
    
    //결과
    //2
    //2
    //7
    //7

     

    위의 singleton1,singleton2를 2개 생성한 이유는 각각 같은 인스턴스인지를 비교하기 위해 2개의 인스턴스를 생성하도록 했다. 보는 것과 같이 new Singleton()같은 생성자 방식이 아니라, getInstance()라는 static method를 통해 인스턴스를 생성하여 참조하도록 하였다.

     

    Singleton class가 스피커 객체라고 가정하고 볼륨을 입력하였다.

    singleton1의 setVolume()을 통해 2를 셋팅했다.

    singleton1.getVolume()으로 값을 출력하면 2가 정상적으로 출력된다.

     

    그렇다면 singleton2는?

     

    singleton2.getVolume()을 하면 이 참조변수 역시 2를 출력한다.

     

    싱글톤패턴에 의해 인스턴스가 1개만 생성되었고 이로 인해 singleton1로 volume값을 할당한 것을 singleton2도 그대로 참조하기 때문이다.

     

    정리 및 기타

    어려운 패턴은 아니지만 가장 언급이 많이되고 중요한 패턴인 것 같다.

    토비의 스프링에서도 자주 언급되는 패턴이기 때문에 스프링에서의 싱글톤패턴을 학습하는데에 도움이 된다.

    일단 Bean을 관리하는 스프링 컨테이너는 빈 객체를 생성할 때 scope를 싱글톤 스코프로 생성한다.

    스프링 자체가 자바엔터프라이즈개발을 위한 프레임워크이며 기업의 사내시스템 등에 자주 사용되기 때문에 사용자가 많을 수 밖에 없다. 사용자가 많기 때문에 요청시마다 계속 Bean을 새로 생성한다면 리소스 부하가 심해질 것이다. 그렇기 때문에 기본적으로 Singleton scope를 사용하여 Bean을 한 번만 생성하여 프로그램이 끝날 때까지 사용한다.

     

    내 기억이 맞다면 스프링 컨테이너에서 사용되는 Singleton scope는 정확히 설명한 싱글톤패턴처럼 만들어지는 것이 아니라 싱글톤패턴의 방식을 차용하여 내부적으로 만들어진다고 한다. 

     

    가물가물에서 블로그를 참고하였다.(아래 참고란에 링크 확인)

     

    참고 : mangkyu.tistory.com/47, velog.io/@govlmo91

     

    반응형

    댓글