Singleton 패턴이란?

  • 객체가 단 하나만 필요할 때 사용하는 패턴
  • 생성된 객체를 어디에서든지 참조할 수 있어야 한다
    • ex) 설정파일의 설정값을 읽어 사용할 때
    • 따라서 내부 값 변경이 많이 발생하는 클래스의 경우 singleton이 적합하지 않다
      • 값 변경이 많은데 singleton을 써야 하는 경우가 있을까?

주의사항

  • thread-safe한 패턴이 아니기 때문에 다음과 같은 문제가 있다
    • 멤버 변수 접근 시 lock 처리
    • instance 객체 생성 시 동시성 문제 처리
      • 이 경우 생성 시점을 일찍/늦게 둔다든지, lock 처리를 한다든지 여러 방법이 있지만 여기에서는 생략하도록 함

생성 예제

// Header file

class Configuration {
public:
    /**
     * 이 부분은 구현 방식에 따라 달라질 수 있는데, 나는 이 방식을 주로 사용한다.
     * static 변수의 특징은 프로세스 시작 시점에 바로 메모리에 올라온다는 점인데,
     * 이 특징을 사용하여 getInstance() 함수처럼 instance에 대한 반환을 요청하지 않아도 되고,
     * getInsatnce()를 호출할 때마다 instance의 생성 여부를 확인하는 단점을 해소할 수도 있기 때문이다.
     * 
     * 물론 프로세스 시작 이후 한 번도 호출되지 않을 경우 불필요한 메모리 공간을 낭비한다는 단점은 있지만,
     * 동시 접근 문제로 인한 골치를 해결할 수 있다는 것을 더 큰 이점으로 생각하고 이 방법을 주로 사용하는 편이다
     */
    static Configuration instance;

public:
    /**
     * singleton 객체를 참조하는 곳에서 호출할 수 있는 멤버함수를 정의한다
     */
    int getReadCount();
//    std::string getTargetFileName();
    void getTargetFileName(std::string& targetFileName);
    int getTargetPos();

private:
    /**
     * 외부에서 객체 생성을 임의로 할 수 없도록, 생성자와 소멸자는 private으로 지정한다
     */

    /**
     * 생성자
     * 생성자는 당연히 매개변수가 없다
     * 매개변수가 필요하다는 것은, 생성되는 객체마다 다른 데이터가 지정되어야 한다는 의미인데
     * 그건... singleton을 쓰는 메리트가 없지 않을까?
     */
    Configuration();

    /**
     * 객체 생성 시점에 필요한 경우 init을 진행할 수도 있다
     */
    void init();

    /**
     * 소멸자
     */
    virtual ~Configuration();

private:
    // 멤버 변수 정의
    int m_readCount;
    std::string m_targetFileName;
    int m_targetPos;
};
// source file
// 이 코드를 반드시 cpp 파일에 추가해야 오류가 발생하지 않는다
Configuration Configuration::instance;

/**
 * 생성자
 */
Configuration::Configuration() {
    m_readCount = 0;
    m_targetPos = 0;
}

/**
 * 소멸자
 */
Configuration::~Configuration() {
}

/**
 * 초기화 함수
 */
void Configuration::init() {
    // 필요한 초기화 동작을 수행하면 된다

    // TODO
    m_readCount = 1;
    m_targetPos = 10;
    m_targetFileName = "testfile.txt";
}

/**
 * getter
 */
int Configuration::getReadCount() {
    return m_readCount;
}

void Configuration::getTargetFileName(std::string &targetFileName) {
    targetFileName.clear();
    targetFileName = m_targetFileName;
}

int Configuration::getTargetPos() {
    return m_targetPos;
}

'Develop > 디자인패턴' 카테고리의 다른 글

생성 패턴(2) - Factory 패턴  (0) 2021.08.18