flagtags / OOP-study

0 stars 0 forks source link

5장 싱글톤 패턴 #17

Open kkirico opened 5 months ago

kkirico commented 5 months ago

고전적인 싱글톤 패턴

class Singleton {
    private static uniqueInstance: Singleton;

    // 생성자를 private으로 설정
    private contructor(){
    }

    // static 메소드를 통해서 생성한다.
    public static getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return this.uniqueInstance
    }
}

인스턴스가 절대 2개 이상이 되지 않도록 하는 구조

초코홀릭 인스턴스 두개면 예상되는 문제 뭘까?

const a = new ChocolateBoiler();
const b = new ChocolateBoiler();

a.fill();
a.drain();
b.fill();
a.boil();
b.drain();
b.fill();
// any problem?
// 초콜릿 보일러 장치는 하나인데, 인스턴스를 두개 만들면 문제가 생기는 상황!!

정의

클래스 인스턴스를 하나만 만들고 그 인스턴스로의 전역 접근을 제공한다.

getInstance(); 의 null 체크가 별도의 스레드에서 일어나는경우 멀티스레딩 문제 발생 가능합니다

해결책:

  1. synchronize 키워드 사용하기

  2. 인스턴스를 처음부터 만들기

    class Singleton {
        // 처음부터 만듭니다.
        private static uniqueInstance = new Singleton();
    
        // 생성자를 private으로 설정
        private contructor(){
        }
    
        // static 메소드를 통해서 생성한다.
        public static getInstance() {
            return this.uniqueInstance
        }
    }
  3. DCL(Double Checking Locking)을 사용하여 getInstance()에서 동기화하는 부분을 줄입니다.

    class Singleton {
        private volatile static uniqueInstance: Singleton;
    
        private contructor(){}
    
        // static 메소드를 통해서 생성한다.
        public static getInstance() {
            if (uniqueInstance == null) {
                synchronized(Singleton.class) {
                    if (uniqueInstance == null) {
                        uniqueInstance = new Singleton();
                    }
                }
            }
            return this.uniqueInstance
        }
    }
    

    이 방법은 두 번째 null 체크에서 동기화를 하므로, 초기에만 동기화하며 이후에는 동기화 없이 인스턴스를 반환하므로 성능이 향상됩니다.

// js 는 싱글스레드 아님? // ts도 마찬가지 아님?

j03y14 commented 5 months ago

인스턴스를 하나 만들어서 사용하는 객체와 전역 변수의 차이?

전역 변수의 단점

고전적인 싱글턴 패턴

class Singleton {
    // 싱글톤 인스턴스를 저장할 정적 프로퍼티
    private static instance: Singleton;

    // 생성자를 private으로 선언하여 외부에서 인스턴스를 생성하는 것을 막음
    private constructor() {}

    public static getInstance() {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }

        return Singleton.instance;
    }
}

싱글턴을 왜 사용하나?

어떻게 유일한 객체를 보장하나?

초콜릿 보일러

class ChocolateBoiler {
    private empty: boolean;
    private boiled: boolean;

    constructor() {
        this.empty = true;
        this.boiled = false;
    }

    public isEmpty() {
        return this.empty;
    }

    public isBoiled() {
        return this.boiled;
    }

    public fill() {
        if (this.isEmpty()) {
            this.empty = false;
            this.boiled = false;
            // 보일러에 우유/초콜릿 혼합재료를 집어넣음
        }
    }

    public drain() {
        if (!this.isEmpty() && this.isBoiled()) {
            // 혼합재료를 다 끓여서 재료를 다음 단계로 넘김
            this.empty = true;
        }
    }

    public boil() {
        if (!this.isEmpty() && !this.isBoiled()) {
            // 혼합재료를 끓임
            this.boiled = true;
        }
    }
}

인스턴스가 두 개 생긴다면? 두 개의 인스턴스가 하나의 기계를 제어하려고 할 때 문제가 발생할 수 있다.

초콜릿 보일러를 싱글턴으로 바꾸자.

싱글턴 패턴의 정의

싱글턴 패턴은 클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공한다.

싱글턴 패턴의 단점?

  1. 모듈간 의존성이 높아진다. 인터페이스가 아닌 클래스 객체를 미리 생성하고 정적 메서드를 이용해 사용하기 때문에 클래스 사이에 강한 의존성이 생긴다.
  2. solid에 위반되는 경우가 많아진다. 인스턴스를 하나만 생성하기 대문에 여러 책임을 지니게 되는 경우가 많다.
  3. 단위테스트에 문제가 있다. 인스턴스가 공유되기 때문에 여러 테스트에서 사용하려면 매번 인스턴스를 초기화 시켜줘야 한다.
  4. private 생성자 때문에 상속이 어렵다.

대안

요즘에는 spring이나 nest의 ioc container에서 인스턴스 관리를 해서 문제점을 해결한다.