싱글톤 패턴(Singleton Pattern)
- 싱글톤 패턴은 생성패턴(Creational Pattern) 중 하나이다.
- 싱글톤 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴이다.
- 싱글톤 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현방법을 시스템과 분리해준다.
- 하나의 객체만을 생성해 이후에 호출된 곳에서는 생성된 객체를 반환하여 프로그램 전반에서 하나의 인스턴스만을 사용하게 하는 패턴이다.
장점
- 한번의 객체 생성으로 재사용이 가능하여 메모리 낭비를 방지한다.
- 시스템 런타임, 환경 세팅에 대한 정보 등, 인스턴스가 여러개 일 때 생기는 문제를 미연에 방지할 수 있다.
문제점
- 멀티 스레드 환경에서 안전하지 않다. (대처가 가능하다.)
- 객체간의 결함도가 높아지고 변경에 유연하게 대처할 수 없다. 싱글톤 객체가 변경되면 이를 참조하고 있는 모든 값들이 변경되어야 한다.
- 객체 상태를 유지하면 큰 문제가 발생한다.(stateful)
코드
* 여러가지 싱글턴 구현 방법이있다.
- 기본 Singleton
- 이른 초기화(Eager Initialization)
- synchronized 사용
- static inner SIngleton(권장하는 방법)
- enum SIngleton(권장하는 방법)
1. 기본 Singleton
- 단점이 Thread Safe 하지 못하다.
public class SingletonStatic {
private static SingletonStatic instance;
private SingletonStatic() {}
public static SingletonStatic getInstance() {
if(instance == null) {
instance = new SingletonStatic();
}
return instance;
}
}
2. Eager Initialization
- Thread Safe
- 단점이 인스턴스를 미리 만들어 놓는다는 단점이 있다.
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
3. Thread Safe Singleton(synchronized 사용)
- synchronized를 통해서 여러 쓰레드에서 동시에 접근하는 것을 막을 수 있다.(Thread Safe)
- 동기화 처리하는 작업 때문에 성능에 불이익이 생길 수 있다.
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) { instance = new ThreadSafeSingleton();}
return instance;
}
}
4. static inner SIngleton(권장하는 방법)
- SingletonHelper 클래스는 SIngleton 클래스가 Load 될 때에도 Load 되지 않다가 getInstance()를 호출할 때 jvm 메모리에 로드되어 인스턴스를 생성한다. synchronized를 사용하지 않기 때문에 synchronized 키워드 자체에 대한 성능 저하 문제를 해결할 수 있다.
- 단점으로 자바 리플렉션을 이용하면 싱글톤 패턴이 깨진다.
public class BillPughSIngleton {
private BillPughSIngleton(){}
private static class SingletonHelper{
private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
}
public static BillPughSIngleton getInstance(){
return SingletonHelper.INSTANCE;
}
}
객체 상태를 유지했을 경우 주의 (stateful)
BillPughSIngleton.java
public class BillPughSIngleton {
private String apartment;
private BillPughSIngleton(){}
private static class SingletonHelper{
private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
}
public static BillPughSIngleton getInstance(){
return SingletonHelper.INSTANCE;
}
public String getApartment() {
return apartment;
}
public void setApartment(String apartment) {
this.apartment = apartment;
}
}
SIngletonTest.java
@Test
void singleTonStatefulTest() {
BillPughSIngleton instance1 = BillPughSIngleton.getInstance();
BillPughSIngleton instance2 = BillPughSIngleton.getInstance();
instance1.setApartment("아크로빌 아파트");
instance2.setApartment("한남더힐 아파트");
System.out.println(instance1.getApartment());
System.out.println(instance2.getApartment());
}
- stateful하게 코드를 작성하게 되면 위와 같이 객체의 상태가 바뀌는 문제가 발생하게 된다.
무결성하게(stateless) 코드를 작성하여야 한다.
BillPughSIngleton.java
- setApartment 값을 바로 return 한다.
public class BillPughSIngleton {
private String apartment;
private BillPughSIngleton(){}
private static class SingletonHelper{
private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
}
public static BillPughSIngleton getInstance(){
return SingletonHelper.INSTANCE;
}
public String getApartment() {
return apartment;
}
public String setApartment(String apartment) {
this.apartment = apartment;
return apartment;
}
}
SIngletonTest.java
@Test
void singleTonStatefulTest() {
BillPughSIngleton instance1 = BillPughSIngleton.getInstance();
BillPughSIngleton instance2 = BillPughSIngleton.getInstance();
String apartment1 = instance1.setApartment("아크로빌 아파트");
String apartment2 = instance2.setApartment("한남더힐 아파트");
System.out.println(apartment1);
System.out.println(apartment2);
}
5. enum Singleton(권장하는 방법)
- 4번(static inner Singleton)과 같이 권장하는 방법으로 enum 클래스로 사용하는 방법이다.
- 자바 리플랙션을 이용해도 enum 클래스는 newInstance를 이용해서 새로운 인스턴스를 생성할 수 없어서 더 안전하다.
- 단점으로는 상속을 사용하지 못하고, 사용하지 않아도 미리 만들어 놓는다는 단점이 있다.
public enum ThreadSafeV5 {
INSTANCE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
실무 사용 사례
1. 자바 java.lang.Runtime 클래스가 싱글톤 패턴으로 구현되어 있다.
2. java Application 개발에서 많이 사용되는 스프링 프레임워크는 스프링 빈 컨테이너의 도움을 받아 싱글톤 스코프로 관리된다.
스프링 프레임워크를 사용하면 싱글톤 패턴의 문제점들을 보완하면서 장점을 누릴수 있다.
3. 다른 디자인 패턴(빌더, 퍼사드, 추상 팩토리 등) 구현체의 일부로 쓰이기도 한다.
'JAVA > Design Pettern' 카테고리의 다른 글
[Design Pattern] 프록시 패턴 (0) | 2022.03.22 |
---|---|
[Design Pattern] 추상 팩토리 패턴 (0) | 2022.03.19 |
[Design Pattern] 어댑터 패턴 (0) | 2022.03.16 |
[Design Pattern] 빌더 패턴 (0) | 2022.03.14 |
[Design Pattern] 팩토리 메소드 패턴 (0) | 2022.03.12 |