싱글톤 패턴(Singleton Pattern)은 무엇인가?
public class Singleton {
private static Singleton instance;
// 생성자를 private으로 선언하여 외부에서의 인스턴스화를 방지
private Singleton() {}
// 인스턴스에 접근하기 위한 전역 접근점
public static Singleton getInstance() {
if (instance == null) { // 인스턴스가 아직 생성되지 않았을 경우에만 생성
instance = new Singleton();
}
return instance;
}
}
장점
I/O Bound
자원을 관리하는데 특히 유용함. 이러한 자원들은 시스템의 성능에 상당한 영향을 미치며, 동시에 여러 접근이 발생할 경우 성능 저하나 불필요한 자원 소모를 일으킬 수 있음. DB(Database)
동시 요청 처리 과정
- 요청 수신 : 백엔드 시스템이 동시에 100개의 요청을 받음
- 연결 풀에서 연결 획득 : 각 요청에 대해, 연결 풀에서 사용 가능한(DB와 연결된) 연결을 하나씩 획득함. 연결 풀의 크기가 충분하면, 요청마다 즉시 연결을 할당받을 수 있음
- DB 작업 실행 : 할당받은 연결을 사용하여 각 요청에 해당하는 DB 작업(쿼리 등)을 실행함
- 연결 반환 : DB 작업이 완료되면, 사용했던 연결을 연결 풀로 반환, 반환된 연결은 다시 사용 가능한 상태가 되며, 다른 요청에서 재사용 될 수 있음
연결 풀의 관리
Network
FileSystem I/O
싱글톤 패턴 - Java
class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
public class HelloWorld{
public static void main(String []args){
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
if (a == b){
System.out.println(true);
}
}
}
단점
TDD(Test Driven Development)
를 할 때 걸림돌이 됨. TDD를 할 때 단위 테스트를 주로 하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며 테스트를 어떤 순서로든 실행할 수 있어야 함독립적인
인스턴스를 만들기가 어려움const assert = require("assert");
const a = [1, 2, 3];
describe("Array", function () {
describe("#indexOf()", function () {
it("should return -1 when the value is not present", function () {
assert.equal(a.indexOf(4), -1);
a[0] = 4;
});
});
describe("#indexOf()", function () {
it("should return -1 when the value is not present", function () {
assert.equal(a.indexOf(4), -1);
});
});
});
위, 아래 로직 순서를 바꾸고 npm test 실행
const assert = require("assert");
const a = [1, 2, 3];
describe("Array", function () {
describe("#indexOf()", function () {
it("should return -1 when the value is not present", function () {
assert.equal(a.indexOf(4), -1);
});
});
describe("#indexOf()", function () {
it("should return -1 when the value is not present", function () {
assert.equal(a.indexOf(4), -1);
a[0] = 4;
});
});
});
- 위의 두 예시는 싱글톤 패턴의 인스턴스는 아니지만 테스트 중에 공유 상태를 변경하는 것이 후속 테스트에 미치는 영향을 잘 보여주는 예이다.
- 테스트 케이스들이 공유 상태에 의존하게 되면, 한 테스트의 실행 결과가 다른 테스트에 영향을 미치게 된다. 이는 싱글톤 인스턴스에서도 비슷하게 나타날 수 있는데, 싱글톤 인스턴스가 변경된 상태를 유지하므로 각 테스트가 독립적으로 실행되지 않고 이전 테스트의 상태에 영향을 받을 수 잇기 때문이다.
- 따라서, 테스트 간의 결합도를 낮추고 독립성을 유지하는 것이 중요하며 테스트 케이스를 작성할 때, 각 테스트가 격리되어야 하고, 다른 테스트에 의존하지 않아야 한다