🤔언제 사용하나요?
1. 주로 공통된 객체를 여러 곳에서 참조해야 하는 경우
EX) 데이터베이스에서 커넥션풀, 스레드풀, 캐시, 로그 기록 객체
EX) 안드로이드 앱 : 각 액티비티 들이나, 클래스마다 주요 클래스들을 하나하나 전달하는게 번거롭기 때문에 싱글톤 클래스를 만들어 어디서든 접근하도록 설계
2. 인스턴스가 절대적으로 한 개만 존재하는 것을 보증하고 싶은 경우
EX) 컴퓨터 자체, 현재의 시스템 설정, 윈도우 시스템),
의존성(=종속성)
A가 B에 의존성이 있다는 것은 B의 변경 사항에 대해 A 또한 변해야 된다는 것을 의미
의존성 주입(DI, Dependency Injection)
- 의존성 주입자(dependency injector)가 '직접' 다른 하위 모듈에 대한 의존성을 주고 메인 모듈이 '간접'적으로 의존성을 주입
- 메인 모듈은 하위 모듈에 대한 의존성이 떨어짐 → 디커플링이 된다.
- 원칙
• 상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않아야 함
• 둘 다 추상화에 의존해야 하며, 이때 추상화는 세부 사항에 의존하지 말아야 함
TDD(Test Driven Development)에서 단위 테스트를 할 때 힘들다
: 단위 테스트를 할 때 테스트는 서로 독립적이어야 함. 그러나 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 각 테스트마다 '독립적인' 인스턴스를 만들기 어려움
멀티 스레드 환경에서 동기화 처리를 하지 않았을 때, 인스턴스가 2개가 생성되는 문제 -> 해결법
public class SingleObject {
//create an object of SingleObject
private static SingleObject instance = new SingleObject(); // 인스턴스 생성
//make the constructor private so that this class cannot be
//instantiated -> 즉 외부에서 생성될 수 없음!
private SingleObject(){}
//Get the only object available
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
public class SingletonPatternDemo {
public static void main(String[] args) {
//illegal construct
//Compile Time Error: The constructor SingleObject() is not visible
//SingleObject object = new SingleObject();
//Get the only object available
SingleObject object = SingleObject.getInstance();
//show the message
// Hello World
object.showMessage();
}
}
즉 클래스가 호출될 때가 아닌 정적 메서드(getInstance)가 처음 호출될 때 인스턴스가 생성된다.
public class SingleObject {
private static SingleObject instance;
private SingleObject() {}
public static SingleObject getInstance() {
if(instance == null) { // 인스턴스가 생성되지 않았으면 생성!
instance = new SingleObject();
}
return instance;
}
}
위의 코드는 멀티스레드 프로그램에서 여러 인스턴스가 생성되는 경합 조건이 발생할 수 있다.
다음 자바 예제는 스레드 세이프 구현으로, 이중 체크 잠금으로 느린 초기화를 사용한다
public class SingleObject {
private static volatile SingleObject instance = null;
private SingleObject() {}
public static SingleObject getInstance() {
if (instance == null) {
synchronized(SingleObject.class) { // class 단위로 lock을 해준다.
if (instance == null) {
instance = new SingleObject();
}
}
}
return instance;
}
}
class Singleton {
private static Singleton instance;
private String name;
private int count = 0;
private Singleton() {
count++; // 다른 인스턴스가 생성되면 해당 count가 증가할 것임.
name = "This is the only reference : " + count;
}
public static Singleton getInstance() {
if(instance == null){
instance = new Singleton(); // 인스턴스가 존재하지 않을 때만 인스턴스 생성
}
return instance; // 기존의 인스턴스 return
}
public String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
Singleton singleton_reference1 = Singleton.getInstance();
System.out.println("First Instance Name : "+singleton_reference1.getName());
Singleton singleton_reference2 = Singleton.getInstance();
System.out.println("Second Instance Name : "+singleton_reference2.getName());
}
}
{}
2. new Object
로 객체를 생성할 때 이 자체만으로 싱글톤 패턴을 구현할 수 있다. 🤔 Why?
클래스 정의와 동시에 인스턴스가 자동으로 생성되기 때문
자바스크립트에 대해서1
{} vs new Object
const obj = {
a: 27
}
console.log(obj === obj)
// true
// 객체 정의와 동시에 인스턴스로서 사용, 더 이상 인스턴스를 추가할 수 없음
class Singleton {
constructor() { // 생성자
if (!Singleton.instance) { // 인스턴스가 존재하지 않으면
Singleton.instance = this // 이 객체를 인스턴스로 지정
}
return Singleton.instance // 존재하면 바로 인스턴스 return
}
getInstance() {
return this
}
}
const a = new Singleton()
const b = new Singleton()
console.log(a === b) // true
싱글톤 패턴은 데이터베이스 연결 모듈에 많이 쓰인다. 이를 통해 데이터베이스 연결에 관한 인스턴스 생성 비용을 아낄 수 있다.
// DB 연결을 하는 것이기 때문에 비용이 더 높은 작업
const URL = 'mongodb://localhost:27017/kundolapp'
const createConnection = url => ({"url" : url}) // DB를 연결해주는 함수, url 속성이 담긴 객체를 전달한다.
class DB {
constructor(url) {
if (!DB.instance) {
DB.instance = createConnection(url)
}
return DB.instance
}
connect() {
return this.instance
}
}
const a = new DB(URL)
const b = new DB(URL)
console.log(a === b) // true
console.log(a.url) // mongodb://localhost:27017/kundolapp
// connect() 함수 구현 코드
Mongoose.prototype.connect = function(uri, options, callback){
const _mongoose = this instanceof Mongoose ? this : mongoose; // 인스턴스 존재하면 이거 없으면 mongoose 생성
const conn = _mongoose.connection; // 인스턴스의 connection 속성
return _mongoose._promiseOrCallback(callback, cb =>{
conn.openUri(uri, options, err => {
if(err != null){ // 에러 발생시
return cb(err);
}
return cb(null, _mongoose); // 에러 발생 안 한 경우
});
});
};
// 메인 모듈 - DB 연결에 관한 인스턴스 정의, 객체 정의와 동시에 인스턴스 생성
const mysql = require('mysql');
const pool = mysql.createPool({
connectionLimit: 10,
host: 'example.org',
user: 'kundol',
password: 'secret',
database : '승철이디비'
});
pool.connect();
// 모듈 A
pool.query(query, function (error, results, fields){
if(error) throw error;
console.log('The solution is: ', results[0].solution);
});
// 모듈 B
pool.query(query, function (error, results, fields){
if(error) throw error;
console.log('The solution is: ', results[0].solution);
});
면접을 위한 CS 전공지식 노트
Java 언어로 배우는 디자인 패턴 입문
GOF Design Pattern
디자인패턴 이해하기
Singleton pattern
Tech Interview for developer
[Design Pattern] GoF(Gang of Four) 디자인 패턴