[C++] 싱글톤 디자인 패턴

tahn·2023년 2월 25일
0

C++

목록 보기
8/8
post-thumbnail

싱글톤 패턴(Singleton Pattern) 정의

싱글톤 패턴은 클래스(객체)가 최초 한번만 메모리를 할당하고 그 메모리에 인스턴스(객체 할당)를 만들어 사용하는 디자인 패턴입니다.

예를들어 로그인 기능이 있는 웹서비스 에서 동시에 회원정보를 변경했을 때, 회원정보 DB가 동시에 변경되게 되는데 각 사용자마다 객체를 따로 두면 각기 다른 객체가 다른 정보를 갖게 됨으로 문제가 발생한다. 따라서 객체가 프로그램에서 한번만 실행되도록 강제하는 패턴입니다.

싱글톤 패턴의 장점

  • 한번의 new로 인스턴스를 사용하기 때문에 메모리 낭비를 방지 (고정된 메모리 영역을 얻음)
  • 싱글톤으로 만들어진 클래스의 인스턴스는 전역이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다.
  • 인스턴스가 절대적으로 한개만 존재하는것을 보증하고 싶을때 쓴다.
  • 두번째 이용시 부터는 객체 로딩 시간이 줄어 성능이 좋아지는 장점이 있다.

이러한 장점을 가진 싱글톤 패턴은 DBCP(DataBaseCommectionPool)처럼 공통된 객체를 여러개 생성해서 사용해야 하는 상황에 많이 사용됩니다.

싱글톤 패턴의 단점

싱글톤 객체가 너무 많은 일을 하거나 많은 데이터를 공유시킬 경우에 다른 클래스의 인스턴스들 간에 결합도가 높아져 “개방-폐쇄 원칙”을 위배하게 됩니다.

이는 자칫 객체지향 설계 원칙에 어긋나기 때문에 수정이 어려워지고 유지보수의 비용이 높아질 수 있습니다.

또한 멀티쓰레드 환경에서 동기화 처리를 안하면 인스턴스가 2개 생성될 수 있는 가능성이 생기게 됩니다.

이러한 이유로 싱글톤 패턴은 꼭 필요한 경우가 아니라면 지양해야 합니다.

싱글톤 패턴의 예제

public class Singleton {

    private static Singleton instance = new Singleton();
    
    private Singleton() {
        // 생성자는 외부에서 호출못하게 private 으로 지정해야 한다.
    }

    public static Singleton getInstance() {
        return instance;
    }

    public void say() {
        System.out.println("hi, there");
    }
}

int main()
{
		Singleton::getInstance()->say();
}

조건은 다음과 같습니다.

  1. 객체를 data 영역에 선언해 행여 발생될 데이터의 해제를 방지할 수 있습니다.
  2. 복사생성자, 생성자, 소멸자를 private 으로 선언하여 복사, 상속을 못하게 막았습니다.

실사용 예제 2 (로거)

#pragma once
#include <fstream>
#include <string>

const std::string LOG_PATH = "C:\\PSData\\logs";

enum EN_LOGLEVEL
{
	enDebug,
	enInfo,
	enWarning,
	enError,
};

class CLogger
{
//// Singleton
public:
	static CLogger* getInstance()
	{
		if ( !instance )
			instance = new CLogger();
		return instance;
	}

	void write(const EN_LOGLEVEL& level, const int& line, const char* func, const char* msg, ...);

private:
	CLogger();
	~CLogger();
	static CLogger* instance;

//// General
public:
	void setLogLevel(const std::string& str);

private:
	EN_LOGLEVEL logLevel;
	std::string fileDate;
	std::ofstream logFile;
	std::string logPath;
};

//외부 클래스에서

CLogger::getInstance()->write(enError, __LINE__, __FUNCTION__, "Not enough Ingredient : %s", emptyIngredient.c_str());

여기서도 마찬가지로 생성자와 소멸자는 private에 작성하였고, 외부에선 아래와 같이 사용하였습니다.

멀티 쓰레드에서의 싱글톤 문제

멀티쓰레드 환경에서 싱글톤을 사용하게 된다면 다음과 같은 문제가 발생할 수 있습니다.

1. 여러개의 인스턴스 생성

Multi-thread환경에서 instance가 없을 때 동시에 아래의 getInstance()메서드를 실행하는 경우 각각 새로운 instance를 생성할 수 있습니다.

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

2. 변수 값의 일관성 실패

다음과 같은 코드가 실행이 되었을 때 여러개의 thread에서 plusCount()를 동시에 실행을 한다면 일관되지 않은 값들이 생길 수 있습니다.

public class Singleton {
    private static Singleton instance;
    private static int count = 0;

    private Singleton() {
    }

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

    public static void plusCount() {
        count++;
    }
}

멀티 쓰레드에서의 싱글톤 해결

1. 정적 변수선언에서 인스턴스 생성

이러한 문제는 아래와 같이 static 변수로 singleton 인스턴스를 생성하는 방법으로 해결할 수 있습니다. 아래와 같이 초기에 인스턴스를 생성하게 된다면 multi-thread환경에서도 다른 객체들은 getInstance를 통해 하나의 인스턴스를 공유할 수 있습니다.

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
}

2. synchronzied의 사용

아래의 코드와 같이 synchronzied를 적용하여 multi-thread에서의 동시성 문제를 해결하는 방법입니다. 하지만 해당 방법은 Thread-safe를 보장하기 위해 성능 저하가 발생할 것입니다.

public class Singleton {
    public class Singleton {
        private static Singleton instance;

        private Singleton() {}

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

결론

싱글톤 패턴은 메모리, 속도, 데이터 공유 측면에서 이점이 있습니다. 하지만 그렇다고 해서 싱글톤 패턴이 무조건 좋은 것은 아닙니다. 앞서 말했듯이 multi-thread환경에서는 동시성 문제가 발생할 수 있기에 싱글톤 패턴을 사용하고자 한다면 사용하기 앞서 "해당 객체의 인스턴스가 한 개만 존재하여야 하는지?"의 여부와 "사용을 하였을 때의 동시성 문제가 발생하지 않는지"를 체크를 하며 사용해야 할 것 같습니다.

profile
html 개발자

0개의 댓글