싱글톤 패턴은 한번만 만들 수 있는 클래스면서 전역에서 접근할 수 있는 클래스를 말한다.
single instance
는 앱 내 어디에서든지 접근할 수 있기 때문에 전역상태를 관리하는데 유용하다
먼저 Counter
라는 클래스를 만들면 다음과 같다
let counter = 0;
class Counter {
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
이렇게 만들어진 Class의 인스턴스를 생성하면 다음과 같이 한 개가 아닌 여러개의 인스턴스를 만들 수 있고, counter1, counter2
의 인스턴스는 동일하지도 않게 된다.
const counter1 = new Counter()
const counter2 = new Counter()
그래서 인스턴스를 생성할 때 인스턴스가 하나 존재한다면 만들지 못하게 에러를 뱉도록 추가해주어야 한다.
let instance;
let counter = 0;
class Counter {
constructor() {
if (instance) {
throw new Error("You can only create one instance!");
}
instance = this;
}
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
const singletonCounter = Object.freeze(new Counter());
export default singletonCounter;
그리고 만들어진 instance를 Object.freeze()
해서 모듈을 export
한다. freeze()
를 통해 데이터를 변경하려는 실수등을 방지할 수 있다.
인스턴스를 한번만 만들 수 있게 제한하면 메모리 공간을 아낄 수 있지만, 사실 싱글톤은 anti-pattern
으로 여겨진다.
java나 c++과 같은 객체지향 프로그래밍 언어에서는 js와 같이 바로 객체를 만들 수 없고, 객체를 만드는 클래스를 만들어야 한다. 그리고 만들어진 객체가 클래스의 인스턴스를 가지는데, 바로 이 클래스의 인스턴스가 자바스크립트에서의 instance
가 된다.
사실 js에서 바로 객체를 만들 수 있기 때문에 위의 예제는 과하다 볼 수 있다. 다음과 같이 객체로 만들어도 똑같기 때문이다.
let count = 0;
const counter = {
increment() {
return ++count;
},
decrement() {
return --count;
}
};
Object.freeze(counter);
export { counter };
counter.js
모듈을 import
할때 해당 모듈이 싱글톤인지 아닌지 명확하지 않을 수 있다. 이 경우 실수로 싱글톤의 value
를 변경할 수도 있는데 이런식으로 superCounter
를 만들어서 dependency를 숨길 수도 있다
import Counter from ".counter";
export default class superCounter {
constructor() {
this.count = 0;
}
increment(){
Counter.increment();
return (this.count+=100);
}
//...
}
Object.freeze()
는 객체를 불변하게 만들고 싶을때 쓸 수 있지만, 얕은 동결이기 때문에, 중첩된 객체까지 동결하게 만들고 싶다면 다음과 같이 함수를 구현하여 동결해야 한다.
function deepFreeze(object) {
// 객체에 정의된 속성명을 추출
var propNames = Object.getOwnPropertyNames(object);
// 스스로를 동결하기 전에 속성을 동결
for (let name of propNames) {
let value = object[name];
object[name] =
value && typeof value === "object" ? deepFreeze(value) : value;
}
return Object.freeze(object);
}
출처:
https://www.patterns.dev/vanilla/singleton-pattern
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze