[디자인 패턴] Singleton 패턴

Narcoker·2023년 7월 25일
0

디자인 패턴

목록 보기
1/8

Singleton 패턴

클래스의 인스턴스가 하나만 생성되도록 보장하는 패턴이다.

이 패턴은 특정 클래스의 객체를 프로그램 내에서 한 개만 만들어 사용하고 싶을 때 사용된다

예를 들면, 시스템 내에서 하나만 존재하는 리소스에 접근하는 경우인데
로깅, 드라이버 객체, 캐시 등에 싱글톤 패턴이 주로 사용된다.

Singleton 패턴을 언제 사용해야하는가?

공유 리소스에 대한 접근 제어

데이터베이스 연결, 파일 시스템 등과 같은 공유 리소스에 대한 동시 접근을 제어하려는 경우에 유용합니다.
싱글톤 패턴을 사용하면 모든 클라이언트에 대해 일관된 접근 방식을 제공하고,
이런 리소스를 보호할 수 있습니다.

로거 객체

로거 객체는 애플리케이션의 다른 부분에서 공유되고 재사용되는데, 이 경우 싱글톤 패턴이 적합하다.
싱글톤 패턴을 사용하면 애플리케이션 전체에서 로거 객체의 인스턴스를 공유할 수 있고,
이를 통해 일관된 로그 메시지를 생성할 수 있습니다.

설정 객체

설정 객체는 애플리케이션 내에서 여러 곳에서 공유되는 정보를 포함하므로, 이를 싱글톤으로 관리하는 것이 유용합니다.

캐시

시스템 내에서 공유되는 캐시를 관리하는 데 싱글톤 패턴이 사용될 수 있습니다.

하드웨어 인터페이스 접근

하드웨어, 네트워크 연결, 그래픽 카드의 특정 인터페이스 등과 같은 리소스에 대한 접근을 제어할 때 싱글톤 패턴이 유용합니다.

싱글톤 패턴을 사용하면 좋은 이유

메모리 측면

최초 한번의 new 연산자를 통해서 고정된 메모리 영역을 사용학 대문에
추후 해당 객체에 접근할 때 메모리 낭비를 방지할 수 있다.

뿐만 아니라 이미 생성된 인스턴스를 활용하여 속도 측면에서도 이점이 있다.

데이터 공유가 쉬워진다.

싱글톤 인스턴스가 전역으로 사용되는 인스턴스이기 때문에
다른 클래서의 인스턴스들이 접근하여 하용할 수 있다.

하지만 여러 클래스의 인스턴스에서 싱글톤 인스턴스의 데이터에 동시에 접근하게되면
동시성 문제가 발생할 수 잇으니 이점을 유의해서 설계해야한다.

Singleton 패턴의 문제점

싱글톤 패턴을 사용하면 많은 문제점을 수반하기 때문에
trade-off 를 잘 고려해야 한다.

많은 코드량

코드 자체가 많이 필요하다.

정적 패토리 메서드에서 객체 생성을 확인하고 생성자를 호출하는 경우에
멀티스레딩 환경에서 발생할 수 있는 동시성 문제를 위해
syncronized 키워드를 사용해야한다.

테스트하기 어려움

싱글톤 인스턴스는 자원을 공유하고 있기 때문에
테스트가 결정적으로 격리된 환경에서 수행되려면
매번 인스턴스의 상태를 초기화 시켜주어야 한다.

그렇지 않으면 어플리케이션 전역에서 상태를 공유하기 때문에
테스트가 온전하게 수행되지 않는다.

구체 클래스의 의존

의존 과녜상 클라이언트가 구체 클래스에 의존하게 된다.

new 키워드를 직접 사용하여 클래스 안에서 객체를 생성하고 있으므로
SOLID 원칙 중 DIP을 위반하게 되고 OCP 원칙 또한 위반할 가능성이 높다.

SOLID - DIP
Dependency Inversion Principle (의존성 역전 원칙)의 약자이다.

  1. 상위 수준의 모듈은 하위 수준의 모듈에 직접 의존하면 안 된다.
    상위 수준과 하위 수준 모두 추상화에 의존해야 한다.

예를 들어, 자동차 클래스가 엔진 클래스에 의존하고 있다고 가정하자.
DIP를 적용하지 않으면, 자동차는 특정 엔진 클래스 (예: 가솔린 엔진)에 직접적으로 의존하게 된다.
그러나 이는 자동차를 다른 타입의 엔진 (예: 전기 엔진)과 함께 사용하는 것을 어렵게 만든다.

  1. 추상화는 세부 사항에 의존하면 안 된다.
    세부 사항이 추상화에 의존해야 한다.

SamsungPay, KakaoPay 등이 있는데 KakaoPay의 기능이 바뀐다고 Pay가 영향을 받아서는 안된다

자식 클래스를 만들 수 없다.

Private 생성자 때문에 자식클래스를 만들기 어렵다.

내부 상태를 변경하기 어렵다.

내부 속성을 변경하거나 초기화하기 어렵다.

구현

Java

public class Singleton {
    private static Singleton instance;
    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

getInstance 메서드를 사용하여 Singleton 클래스의 인스턴스에 접근할 수 있게 한다.
Singleton 클래스의 생성자는 private으로 선언되어,
클래스 외부에서 new 키워드를 사용하여 새로운 인스턴스를 만들 수 없도록 한다.
이렇게 하여 해당 클래스의 인스턴스가 오직 하나만 존재하도록 보장한다.

Javascript

클로저를 활용한 Singleton 패턴

var Singleton = (function () {
    var instance;
 
    function createInstance() {
        var object = new Object("I am the instance");
        return object;
    }
 
    return {
        getInstance: function () {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();
 
var instance1 = Singleton.getInstance();
var instance2 = Singleton.getInstance();
 
console.log("Same instance? " + (instance1 === instance2));  // logs: "Same instance? true"

이 코드는 IIFE(즉시 실행 함수 표현식)을 사용하여 Singleton 객체를 생성한다.

getInstance 메서드를 통해 Singleton 인스턴스에 접근할 수 있다.
createInstance 함수는 클로저로 구현된 private 함수로, 외부에서 접근할 수 없으며
getInstance 함수 내에서만 호출됩니다.

ES6의 class 문법을 사용한 Singleton 패턴

class Singleton {
  constructor() {
    if (!Singleton.instance) {
      Singleton.instance = this;
    }
    return Singleton.instance;
  }
}

const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2);  // true

Singleton 클래스의 생성자에서 Singleton.instance가 존재하는지 체크하고,
없을 경우 this (즉, 새로 생성된 인스턴스)를 Singleton.instance에 할당한다.

이후에 다시 Singleton 클래스로부터 인스턴스를 생성하려고 하면
생성자에서 Singleton.instance를 반환하여 항상 같은 인스턴스를 얻을 수 있게 된다.

참고

profile
열정, 끈기, 집념의 Frontend Developer

0개의 댓글