1. 싱글톤 패턴이란?
인스턴스를 오직 한개만 제공하는 클래스
This pattern involves a single class which is responsible to create an object while making sure that only single object gets created. This class provides a way to access its only object which can be accessed directly without need to instantiate the object of the class.
reference : https://www.tutorialspoint.com/design_pattern/singleton_pattern.htm
*인스턴스 : 객체 지향 프로그래밍(OOP)에서 클래스(class)에 소속된 개별적인 객체를 말한다
*클래스 : 객체를 정의하는 틀 또는 설계도
사용 시기
- 클래스의 인스턴스가 오직 하나여야 함을 보장하고, 잘 정의된 접근점으로 모든 사용자가 접근할 수 있도록 해야할 때
- 유일한 인스턴스가 서브클래싱으로 확장되어야 하며, 사용자는 코드의 수정없이 확장된 서브클래스의 인스턴스를 사용할 수 있어야 할 때
장점
- 유일하게 존재하는 인스턴스로 접근 통제 : 캡슐화를 통해 언제 어떻게 접근하는지를 제어 가능
- 이름 공간을 좁힘 : 후에 생길 디버깅 문제를 미연에 차단
- 연산 및 표현의 정제 허용 : 상속이 가능하고 이를 상속한 서브클래스를 통해 새로운 인스턴스을 만들 있다.
- 인스턴스 개수 변경 용이
- 클래스 연산보다 훨씬 유연함
2. 구현 - 1 : 쌩기본(?) singleton
Singleton.java
package designpattern.designpattern.singleton;
public class Singleton {
public static void main(String[] args) {
//global 하게 하나의 instance를 공유해서 사용할 것임.
// SingletonObject so = new SingletonObject(); <= 오류남. private 설정이 되어있어 생성 불가
SingletonObject instance = SingletonObject.getInstance();
System.out.println(instance == SingletonObject.getInstance());
}
}
SingletonObject.java
package designpattern.designpattern.singleton;
public class SingletonObject {
private static SingletonObject instance = null;
//외부에서 인스턴스를 생성 못하게 private 설정
private SingletonObject(){}
public static SingletonObject getInstance() {
//맨 처음 인스턴스가 없으면 새로 생성해서 static 변수로 할당
if(instance == null){
instance = new SingletonObject();
}
//존재하면
return instance;
}
}
3. 문제점
What will happen if two threads call getInstance() method at the same time?
In that case the second thread is going to create a new instance of Singleton
class and override the existing instance which has been created by the first thread. In order to improve that we are going to implement a synchronization block.
=> 두개의 쓰레드가 동시에 접근시에 객체를 따로 생성할 가능성이 있음.
=> 멀티스레드에서도 안전한 코드를 구현해야 함
3. 구현 - 2 : synchronize 이용
Setting2.java
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
/**
* synchronized 사용해서 동기화 처리
*/
public class Settings2 {
private static Settings2 instance;
private Settings2() { }
public static synchronized Settings2 getInstance() {
if (instance == null) {
instance = new Settings2();
}
return instance;
}
}
단점 : 성능저하
=>getInstance를 호출할 때 마다 동기화 처리 작업이 일어나서 성능이 저하될 수 있음
setting2.java - 수정 : 이른 초기화 사용하기
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
/**
* 이른 초기화를 통한 싱글톤 패턴 구현
*/
public class Settings2 {
private static final Settings2 INSTANCE = new Settings2();
private Settings2() { }
public static synchronized Settings2 getInstance() {
return INSTANCE;
}
}
=> thread safe함. 상수로 선언해서 미리 만들어둔것을 반환해주는 것이다.
단점 : 미리 만든다는 것 자체가 단점이 될 수 있다.
Settings3.java - double checked locking 사용하기
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
/**
* double checked locking
*/
public class Settings3 {
private static volatile Settings3 instance;
private Settings3() { }
public static Settings3 getInstance() {
if (instance == null) {
synchronized (Settings3.class) {
if (instance == null) {
instance = new Settings3();
}
}
}
return instance;
}
}
이렇게 하면 getInstance()를 호출 할 때 마다 동기화 작업이 생기는게 아니라 정말 관리하는 singleton 인스턴스가 null일 때만 동기화 작업이 이뤄진다.
단점 : 복잡함(volatile도 노이해...)
Settings4.java - static inner 클래스 사용하기
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
/**
* static inner 클래스 홀더
*/
public class Settings4 {
private Settings4() { }
private static class Settings4Holder {
private static final Settings4 INSTANCE = new Settings4();
}
public static Settings4 getInstance() {
return Settings4Holder.INSTANCE;
}
}
getInstance()가 호출될 때 Settings4Holder가 호출되고, 그때에 static final 변수를 생성한다.
=> lazy loading이 가능하다.
이전과 비교해 코드도 간단해진다.
Settings5.java - enum 클래스 사용하기
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
/**
* Enum을 사용해서 싱글톤 만들기
*/
public enum Settings5 {
INSTANCE;
//내부에 property도 정의도 정의 가능
}
장점 : reflection에 안전하다. 직렬화, 역직렬화도 동일한 인스턴스로 가능함.
App.java
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
import java.io.*;
public class App {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Settings5 settings = Settings5.INSTANCE;
Settings5 settings1 = null;
try (ObjectOutput out = new ObjectOutputStream(new FileOutputStream("settings.obj"))) {
out.writeObject(settings);
}
try (ObjectInput in = new ObjectInputStream(new FileInputStream("settings.obj"))) {
settings1 = (Settings5) in.readObject();
}
System.out.println(settings == settings1);
}
}
스프링에서 지원하는 기능
SpringConfig.java
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public String hello() {
return "hello";
}
}
SpringExample.java
package me.whiteship.designpatterns._01_creational_patterns._01_singleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringExample {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
String hello = applicationContext.getBean("hello", String.class);
String hello2 = applicationContext.getBean("hello", String.class);
System.out.println(hello == hello2);
//결과 true
}
}
'디자인 패턴(구) > 생성 패턴' 카테고리의 다른 글
프로토타입(Prototype) 패턴이란? (0) | 2022.04.06 |
---|---|
빌더(Builder) 패턴이란? (2) | 2022.04.06 |
추상 팩토리 패턴(Abstract Factory Pattern)이란? (0) | 2022.04.05 |
팩토리 메서드(Factory Method) 패턴 (4) | 2022.04.03 |