본문 바로가기
Concepts/Design Pattern

객체 생성 패턴- 단일체

by ocwokocw 2021. 4. 12.

- 참조: GoF의 디자인 패턴

- 참조: Effective Java 2/E

- 단일체(Singleton) 패턴

단일체 패턴은 한 개의 클래스 인스턴스만을 갖도록 보장하며, 이에 대해 접근점을 제공한다. 보통 단일체 패턴의 예로는 창 관리자나 파일 시스템과 같은 예가 있다. 가장 단순하게 구현하려면 전역 변수를 선언하면 되지만 이 방식은 문제가 상당히 많다.

 

단일체 패턴을 구현할 때에는 클래스가 자체적으로 인스턴스 접근방법을 관리하도록 한다. 

위의 그림은 단일체 패턴의 구조를 UML로 나타낸것이다. 단일체 패턴은 참여자가 1개밖에 존재하지 않는다.

  • Singleton: Instance() 연산을 정의하여, 유일한 인스턴스로 접근할 수 있도록 한다. 이때 Instance는 클래스 연산(static)이다.

단일체 패턴을 사용하면 접근을 통제한다는 장점이 있다. 또한 전역 변수를 사용하는것보다 디버깅이 쉽다.


- Java 예제

public static 구현: 가장 흔히접할 수 있는 예제는 아래와 같이 private 생성자를 가지면서 public static final 을 이용하여 클래스변수를 선언하는것이다.

 

public class Singleton {

	public static final Singleton INSTNACE = new Singleton();
	
	private Singleton() {}
	
	public String getName() {
		return "Singleton";
	}
}

 

- Main 함수

 

public class DesignPatternTest {

	public static void main(String[] args) {
		
		Singleton singleton = Singleton.INSTNACE;
		System.out.println("singleton: " + singleton);
		System.out.println("singleton name: " + singleton.getName());
		
		Singleton singleton2 = Singleton.INSTNACE;
		System.out.println("singleton2: " + singleton2);
		System.out.println("singleton2 name: " + singleton2.getName());
	}	
}

 

- 결과

singleton: designpattern.singleton.Singleton@2a139a55
singleton name: Singleton
singleton2: designpattern.singleton.Singleton@2a139a55
singleton2 name: Singleton

정적 팩토리 메소드: 또 다른 방법은 정적 팩토리 메소드를 이용하여 구현하는것이다. public static을 private static 으로 변경하고 정적 팩토리 메소드 getInstance 를 추가한다. getInstance를 사용하여 단일체 패턴을 동일하게 구현할 수 있다. 

 

위처럼 public static 으로 그냥 하면되는데 왜 팩토리 메소드를 만들어서 뻘짓을할까? 정적 팩토리 메소드를 사용하면 추후 요구사항 변경으로 인해 생성패턴을 바꿔야할 때 API의 변경없이 변경이 가능하다.

 

public class Singleton {

	private static final Singleton INSTNACE = new Singleton();
	
	private Singleton() {}
	
	public static Singleton getInstnace() {
		return INSTNACE;
	}

	public String getName() {
		return "Singleton";
	}
}

 

위의 방법은 단일체 패턴을 구현하는데에는 문제가 없지만 약간의 허점이 있다. 우선 AccessibleObject.setAccessible 메소드의 도움을 받아(reflection) private 생성자가 호출 가능하다. 

 

또한 직렬화 가능 하게 만들려면 Serializable을 구현해야 한다. 그리고 모든 필드를 transient로 선언하고 readResolve 메소드를 추가해야 한다. 그렇지 않으면 역직렬화될때마다 새로운 객체가 생성된다.


enum 구현: JDK 1.5 이상에서는 원소가 하나인 enum 자료형을 이용할 수 있다. public 과 동일한 방법으로 구현하지만 직렬화가 자동으로 처리되며 reflection 공격에도 안전하다.

 

public enum Singleton {
	
	INSTNACE;
	
	public String getName() {
		return "Singleton";
	}
}

 

댓글