흔히 디자인 패턴을 적용해서 개발하면서 가장 많이 쓰고 있는 패턴이 있다면 싱글턴 패턴일 것이다. (물론 개인적인 기준일 수 있지만)

그만큼 자주 사용하지만 정확히 싱글턴 패턴이란 무엇인지, 왜 사용하는지 설명하려면 어려울 수 있다.

그래서 이번에는 싱글턴 패턴에 대해서 알아보자.

 

싱글턴 패턴이란?

· 오직 한 개의 클래스 인스턴스만을 갖도록 보장하고, 이에 대한 전역적인 접근점을 제공하는 패턴.

※ 단일체 패턴이라고도 불림.

 

· 클래스 자신이 자기의 유일한 인스턴스로 접근하는 방법을 자체적으로 관리하는 것. 이 클래스는 또 다른 인스턴스가 생성되지 않도록 할 수 있고, 클래스 자신이 그 인스턴스에 대한 접근 방법을 제공할 수 있음.

 

언제 싱글턴 패턴을 사용해야 할까?

· 클래스의 인스턴스가 오직 하나여야 함을 보장하고, 잘 정의된 접근점(Access Point)으로 모든 사용자가 접근할 수 있도록 해야 할 때.

· 유일한 인스턴스가 서브클래싱으로 확장되어야 하며, 사용자는 코드의 수정없이 확장된 서브 클래스의 인스턴스를 사용할 수 있어야 할 때.

 

구조

· Singleton : Instance() 연산을 정의하여, 유일한 인스턴스로 접근할 수 있도록 함. Instance() 연산은 유일한 인스턴스를 생성하는 책임을 맡음.

 

싱글턴 패턴의 장점

· Singleton 클래스 자체가 인스턴스를 캡슐화하기 때문에, 이 클래스에서 사용자가 언제, 어떻게 이 인스턴스에 접근할 수 있는지 제어할 수 있음.

· name space를 좁힘. 즉, 전역 변수를 정의하여 발생하는 디버깅의 어려움 등 문제를 없앰.

· 연산 및 표현의 정제를 허용함.

· 인스턴스의 개수를 변경하기가 자유로움.

· 클래스 연산을 사용하는 것보다 훨씬 유연한 방법.

 

싱글턴 패턴의 종류

· Eager Initialization

public class EagerInitialization {
	private static EagerInitialization instance = new EagerInitialization();
	
	private EagerInitialization () {
		
	}
		
	public static EagerInitialization getInstance () {
		return instance;
	}
}

가장 기본적인 싱글턴 패턴 방법. 전역 변수로 인스턴스를 만드는데 private static을 이용.

· Static Block Initialization

public class StaticBlockInitalization {
	private static StaticBlockInitalization instance;
	
	private StaticBlockInitalization () {
		
	}
	
	static {
		try {
			instance = new StaticBlockInitalization();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static StaticBlockInitalization getInstance () {
		return instance;
	}	
}

static 초기화 블럭을 이용하여 클래스가 로딩 될 때, 최초 한번 실행되며 이때 인스턴스를 생성. 인스턴스가 사용되는 시점에 생성 되는 것이 아님

· Lazy Initialization

public class LazyInitialization {
	private static LazyInitialization instance;
	
	private LazyInitialization () {
		
	}
	
	public static LazyInitialization getInstance () {
		if ( instance == null ) {
			instance = new LazyInitialization();
		}
		
		return instance;
	}
}

기본적으로 본인이 가장 많이 사용하는 방법. getInstance()에서 인스턴스가 없을 경우에만 새로 인스턴스를 생성해주는 방법. 하지만 multi thread의 경우 인스턴스가 두번 생길 위험이 존재함.

· Thread Safe Initialization

public class ThreadSafeInitalization {
	private static ThreadSafeInitalization instance;
	
	private ThreadSafeInitalization () {
		
	}
	
	public static synchronized ThreadSafeInitalization getInstance () {
		if (instance == null) {
			instance = new ThreadSafeInitalization();
		}
		
		return instance;
	}
}

위에 있는 Lazy Initialization의 multi thread 문제를 해결하기 위해 synchronized(동기화)를 사용하여 구현함. 높은 cost 비용으로 프로그램 전반에 성능 저하가 일어날 수 있음.

· Initialization on Demand Holder Idiom

public class InitializationOnDemandHolderIdiom {
	private InitializationOnDemandHolderIdiom () {
		
	}
	
	private static class Singleton {
		private static final InitializationOnDemandHolderIdiom instance = new InitializationOnDemandHolderIdiom();
	}
	
	public static InitializationOnDemandHolderIdiom getInstance () {
		return Singleton.instance;
	}
}

Bill pugh라는 컴퓨터 과학 연구원이 기존의 싱글턴 패턴의 문제점들을 해결하기 위해서 만든 새로운 싱글턴 패턴. 이것은 jvm의 class loader 매커니즘과 class load 시점을 이용하여 인스턴스를 생성시킴으로 thread간의 동기화 문제를 해결함. (Bill pugh가 만들었다 해서 Bill pugh Singleton이라고 부르기도 함)

· Enum Initialization

public enum EnumInitialization {
	INSTANCE;
		
	public static EnumInitialization getInstance() {
		return INSTANCE;
	}
}

Joshua Bolch의 Effective Java라는 책에서 소개된 enum 싱글턴 방법.

- enum은 인스턴스가 생성될 때, multi thread로부터 안전함

- 단 한번의 인스턴스 생성을 보장함.

- 또한 enum value는 자바 프로그램 전역에서 접근이 가능함.

따라서 위와 같은 이유로 enum을 싱글턴 패턴으로 사용될 수 있음.

 

※ 종류와 관련된 더욱 자세한 내용은 아래 [참고]에 나와있는 블로그를 봐주시면 되겠습니다.

싱글턴 패턴의 예

· Session

· Cache

· Logger

 

[참고] https://blog.seotory.com/post/2016/03/java-singleton-pattern

[참고] GoF의 디자인 패턴


WRITTEN BY
김치치즈스마일
세계정복!

,