[TIL] 2025-12-22 | C++ 디자인패턴 - 싱글톤, 데코레이터, 옵저버 정리

2025. 12. 22. 13:58·내배캠Unreal_TIL/C++
게임 개발자를 위한 C++ 문법 3-1

1. WHY

  • 디자인 패턴의 개념과 필요성 이해
  • 싱글톤, 데코레이터, 옵저버 패턴

 

2. WHAT

디자인 패턴: 개발 시 반복적으로 등장하는 문제를 해결하기 위한 일반화된 솔루션

생성 패턴: 새로운 것을 만들어내는 방법과 관련된 패턴

구조 패턴: 여러 부품을 어떻게 조립하고 연결하는 방법에 대한 패턴

행동 패턴: 부품이 서로 어떻게 상호작용할지에 대한 패턴

 

- 싱글톤 패턴 Singleton Pattern (생성 패턴)

클래스의 객체를 한개만 생성해서 그 객체만을 사용하는 패턴

class Singleton {
private:
    static Singleton* instance;  // 정적 멤버
    Singleton() {}               // 외부 생성 금지

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

// 정적 멤버 정의 (cpp 파일 또는 전역)
Singleton* Singleton::instance = nullptr;

생성자로 객체 생성 X, getInstance()를 통해 객체 생성을 제어하여 객체가 하나만 존재하도록 함!    single~

 

 

예제: 

#include <iostream>
using namespace std;

class Airplane {
private:
    static Airplane* instance; // 유일한 비행기 객체를 가리킬 정적 포인터
    int positionX;             // 비행기의 X 위치
    int positionY;             // 비행기의 Y 위치

    // private 생성자: 외부에서 객체 생성 금지
    Airplane() : positionX(0), positionY(0) {
        cout << "Airplane Created at (" << positionX << ", " << positionY << ")" << endl;
    }

public:
    // 복사 생성자와 대입 연산자를 삭제하여 복사 방지
    Airplane(const Airplane&) = delete;
    Airplane& operator=(const Airplane&) = delete;

    // 정적 메서드: 유일한 비행기 인스턴스를 반환
    static Airplane* getInstance() {
        if (instance == nullptr) {
            instance = new Airplane();
        }
        return instance;
    }

    // 비행기 위치 이동
    void move(int deltaX, int deltaY) {
        positionX += deltaX;
        positionY += deltaY;
        cout << "Airplane moved to (" << positionX << ", " << positionY << ")" << endl;
    }

    // 현재 위치 출력
    void getPosition() const {
        cout << "Airplane Position: (" << positionX << ", " << positionY << ")" << endl;
    }
};

// 정적 멤버 초기화
Airplane* Airplane::instance = nullptr;

// 메인 함수 (사용 예시)
int main() {
    // 유일한 비행기 인스턴스를 가져옴
    Airplane* airplane = Airplane::getInstance();
    airplane->move(10, 20);  // 비행기 이동
    airplane->getPosition();

    // 또 다른 요청도 같은 인스턴스를 반환
    Airplane* sameAirplane = Airplane::getInstance();
    sameAirplane->move(-5, 10); // 비행기 이동
    sameAirplane->getPosition();

    return 0;
}

 

 

추가:

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

class Singleton {
private:
    Singleton() {}

public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton& getInstance() {
        static Singleton instance;  // C++11 이후 thread-safe
        return instance;
    }
};

 

 

- 데코레이터 패턴 Decorator Pattern (구조 패턴)

객체에 동적으로 기능을 추가하여 확장할 수 있는 구조 패턴, 상속을 통한 클래스 확장이 아닌 객체를 감싸는 방식을 사용해 기능을 추가하거나 변경!

데코레이터 패턴 구조

Component

#include <iostream>
#include <string>

class Component {
public:
    virtual ~Component() = default;
    virtual std::string operation() const = 0;
};

 

ConcreteComponent

class ConcreteComponent : public Component {
public:
    std::string operation() const override {
        return "Base";
    }
};

 

Decorator

class Decorator : public Component {
protected:
    Component* component;  // 감싸는 대상

public:
    explicit Decorator(Component* comp) : component(comp) {}

    std::string operation() const override {
        return component->operation();
    }
};

 

ConcreteDecorator

class ConcreteDecoratorA : public Decorator {
public:
    using Decorator::Decorator;

    std::string operation() const override {
        return "DecoratorA(" + Decorator::operation() + ")";
    }
};

class ConcreteDecoratorB : public Decorator {
public:
    using Decorator::Decorator;

    std::string operation() const override {
        return "DecoratorB(" + Decorator::operation() + ")";
    }
};

 

 

예제: 

#include <iostream>
#include <string>

using namespace std;

// **추상 컴포넌트 (Component): Pizza**
// - 피자 객체의 기본 구조를 정의하는 인터페이스입니다.
// - 모든 피자는 이름(`getName`)과 가격(`getPrice`)을 가져야 합니다.
class Pizza {
public:
    virtual ~Pizza() {}
    virtual string getName() const = 0;  // 피자의 이름 반환
    virtual double getPrice() const = 0; // 피자의 가격 반환
};

// **구체 컴포넌트 (Concrete Component): BasicPizza**
// - 기본 피자 클래스입니다.
// - 피자의 기본 베이스(이름과 가격)를 구현합니다.
class BasicPizza : public Pizza {
public:
    string getName() const {
        return "Basic Pizza"; // 기본 피자의 이름
    }
    double getPrice() const {
        return 5.0; // 기본 피자의 가격
    }
};

// **데코레이터 추상 클래스 (Decorator): PizzaDecorator**
// - 기존 피자의 기능을 확장하기 위한 데코레이터의 기본 구조를 정의합니다.
// - 내부적으로 `Pizza` 객체를 감싸며, 이름과 가격에 추가적인 기능을 제공합니다.
class PizzaDecorator : public Pizza {
protected:
    Pizza* pizza; // 기존의 피자 객체를 참조합니다.
public:
    // 데코레이터는 피자 객체를 받아서 감쌉니다.
    PizzaDecorator(Pizza* p) : pizza(p) {}
    
    // 소멸자에서 내부 피자 객체를 삭제합니다.
    virtual ~PizzaDecorator() {
        delete pizza;
    }
};

// **구체 데코레이터 (Concrete Decorators): Cheese, Pepperoni, Olive**
// - 각각의 토핑 데코레이터는 `PizzaDecorator`를 상속받아 이름과 가격을 확장합니다.

// 치즈 토핑 데코레이터
class CheeseDecorator : public PizzaDecorator {
public:
    CheeseDecorator(Pizza* p) : PizzaDecorator(p) {}
    string getName() const {
        // 기존 피자의 이름에 " + Cheese"를 추가
        return pizza->getName() + " + Cheese";
    }
    double getPrice() const {
        // 기존 피자의 가격에 치즈 추가 비용 1.5를 더함
        return pizza->getPrice() + 1.5;
    }
};

// 페퍼로니 토핑 데코레이터
class PepperoniDecorator : public PizzaDecorator {
public:
    PepperoniDecorator(Pizza* p) : PizzaDecorator(p) {}
    string getName() const {
        // 기존 피자의 이름에 " + Pepperoni"를 추가
        return pizza->getName() + " + Pepperoni";
    }
    double getPrice() const {
        // 기존 피자의 가격에 페퍼로니 추가 비용 2.0을 더함
        return pizza->getPrice() + 2.0;
    }
};

// 올리브 토핑 데코레이터
class OliveDecorator : public PizzaDecorator {
public:
    OliveDecorator(Pizza* p) : PizzaDecorator(p) {}
    string getName() const {
        // 기존 피자의 이름에 " + Olive"를 추가
        return pizza->getName() + " + Olive";
    }
    double getPrice() const {
        // 기존 피자의 가격에 올리브 추가 비용 0.7을 더함
        return pizza->getPrice() + 0.7;
    }
};

// **클라이언트 코드**
// - 피자와 데코레이터를 조합하여 최종 피자를 생성하고, 정보를 출력합니다.
int main() {
    // 1. 기본 피자를 생성합니다.
    Pizza* pizza = new BasicPizza();

    // 2. 치즈 토핑을 추가합니다.
    pizza = new CheeseDecorator(pizza);

    // 3. 페퍼로니 토핑을 추가합니다.
    pizza = new PepperoniDecorator(pizza);

    // 4. 올리브 토핑을 추가합니다.
    pizza = new OliveDecorator(pizza);

    // 5. 최종 피자 정보 출력
    cout << "Pizza: " << pizza->getName() << endl; // 피자의 이름 출력
    cout << "Price: $" << pizza->getPrice() << endl; // 피자의 가격 출력

    // 6. 메모리 해제
    delete pizza;

    return 0;
}

 

- 옵저버 패턴 Observer Pattern (행동 패턴)

객체 간의 일대다 관계를 정의하여 하나의 객체의 상태가 변경될 때 이 상태를 관찰하는 여러 객체(옵저버)에게 자동으로 알림을 보내는 패턴

 

Observer

class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(int state) = 0;
};

 

Subject

#include <vector>
#include <algorithm>

class Subject {
public:
    virtual ~Subject() = default;
    virtual void attach(Observer* obs) = 0;
    virtual void detach(Observer* obs) = 0;
    virtual void notify() = 0;
};

 

ConcreteSubject

class ConcreteSubject : public Subject {
private:
    int state;
    std::vector<Observer*> observers;

public:
    void attach(Observer* obs) override {
        observers.push_back(obs);
    }

    void detach(Observer* obs) override {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end()
        );
    }

    void setState(int s) {
        state = s;
        notify();
    }

    int getState() const {
        return state;
    }

    void notify() override {
        for (auto obs : observers) {
            obs->update(state);
        }
    }
};

 

ConcreteObserver

#include <iostream>

class ConcreteObserver : public Observer {
private:
    std::string name;

public:
    explicit ConcreteObserver(std::string n) : name(n) {}

    void update(int state) override {
        std::cout << "Observer " << name
                  << " notified. New state = "
                  << state << std::endl;
    }
};

 

 

예제: 

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// Observer 인터페이스
// - Observer 패턴에서 상태 변화를 알림받는 객체들의 공통 인터페이스
// - Observer들은 이 인터페이스를 구현하여 `update` 메서드를 통해 데이터를 전달받음
class Observer {
public:
    virtual ~Observer() = default;               // 가상 소멸자
    virtual void update(int data) = 0;           // 데이터 업데이트 메서드 (순수 가상 함수)
};

// Subject 클래스 (엑셀 시트 역할)
// - 데이터의 상태 변화를 관리하며, 모든 등록된 Observer들에게 변경 사항을 알림
class ExcelSheet {
private:
    vector<Observer*> observers;                 // Observer들을 저장하는 리스트
    int data;                                    // 현재 데이터 상태

public:
    ExcelSheet() : data(0) {}                    // 생성자: 초기 데이터 값은 0

    // Observer 등록 메서드
    // - 새로운 Observer를 등록하여 변경 사항 알림을 받을 수 있도록 추가
    void attach(Observer* observer) {
        observers.push_back(observer);
    }

    // 데이터 변경 알림 메서드
    // - 등록된 모든 Observer들의 `update` 메서드를 호출하여 데이터 변경 사항을 알림
    void notify() {
        for (Observer* observer : observers) {
            observer->update(data);              // 각 Observer에게 데이터를 전달
        }
    }

    // 데이터 설정 메서드
    // - 데이터를 변경하고 변경 사항을 모든 Observer에게 알림
    void setData(int newData) {
        data = newData;                          // 새로운 데이터로 갱신
        cout << "ExcelSheet: Data updated to " << data << endl;
        notify();                                // Observer들에게 알림
    }
};

// 구체적인 Observer 클래스: BarChart (막대 차트)
// - 데이터를 막대 그래프로 표현
class BarChart : public Observer {
public:
    void update(int data) {                      // 데이터 업데이트 시 호출됨
        cout << "BarChart: Displaying data as vertical bars: ";
        for (int i = 0; i < data; ++i) {
            cout << "|";                         // 데이터 값만큼 막대 출력
        }
        cout << " (" << data << ")" << endl;
    }
};

// 구체적인 Observer 클래스: LineChart (라인 차트)
// - 데이터를 선형 그래프로 표현
class LineChart : public Observer {
public:
    void update(int data) {                      // 데이터 업데이트 시 호출됨
        cout << "LineChart: Plotting data as a line: ";
        for (int i = 0; i < data; ++i) {
            cout << "-";                         // 데이터 값만큼 선 출력
        }
        cout << " (" << data << ")" << endl;
    }
};

// 구체적인 Observer 클래스: PieChart (파이 차트)
// - 데이터를 파이 그래프로 표현
class PieChart : public Observer {
public:
    void update(int data) {                      // 데이터 업데이트 시 호출됨
        cout << "PieChart: Displaying data as a pie chart slice: ";
        cout << "Pie [" << data << "%]" << endl; // 데이터 값 출력 (가정: % 비율로 표현)
    }
};

// 메인 함수
int main() {
    // Subject 생성
    ExcelSheet excelSheet;                       // 데이터를 관리하는 엑셀 시트 객체 생성

    // Observer 객체 생성 (각 차트 객체)
    BarChart* barChart = new BarChart();         // 막대 차트 생성
    LineChart* lineChart = new LineChart();      // 라인 차트 생성
    PieChart* pieChart = new PieChart();         // 파이 차트 생성

    // Observer 등록
    // - 각 차트(Observer)를 엑셀 시트(Subject)에 등록
    excelSheet.attach(barChart);
    excelSheet.attach(lineChart);
    excelSheet.attach(pieChart);

    // 데이터 변경 테스트
    // - 데이터를 변경하면 등록된 모든 Observer들이 알림을 받고 화면에 갱신
    excelSheet.setData(5);                       // 데이터 변경: 5
    excelSheet.setData(10);                      // 데이터 변경: 10

    // 메모리 해제
    // - 동적 할당된 Observer(차트) 객체 삭제
    delete barChart;
    delete lineChart;
    delete pieChart;

    return 0;
}

 

3. RESULT

  • 숙제 -  OCP원칙이 적용된 Animal 클래스에 기능 추가
#include <iostream>
#include <vector>
using namespace std;

// 기본 Animal 클래스 (수정 금지)
class Animal {
public:
    virtual void speak() = 0; // 순수 가상 함수
    virtual ~Animal() = default; // 가상 소멸자
};

// Dog 클래스 (이미 구현됨)
class Dog : public Animal {
public:
    void speak() {
        cout << "Woof! Woof!" << endl;
    }
};

// Cat 클래스 (이미 구현됨)
class Cat : public Animal {
public:
    void speak() {
        cout << "Meow! Meow!" << endl;
    }
};

//새 동물 클래스 추가
class Hamster : public Animal {
public:
    void speak() {
        cout << "Zzik! Zzik!" << endl;
    }
};
// 동물의 소리를 출력하는 함수 (수정 금지)
void makeAnimalsSpeak(vector<Animal*>& animals) {
    for (Animal* animal : animals) {
        animal->speak();
    }
    // 동적으로 할당된 메모리를 해제합니다.
    for (Animal* animal : animals) {
        delete animal;
    }
}

int main() {
    vector<Animal*> animals;
    animals.push_back(new Dog());
    animals.push_back(new Cat());

    animals.push_back(new Hamster());

    makeAnimalsSpeak(animals);
    return 0;
}

 

4. 참고

https://skianything.tistory.com/24

 

싱글톤 패턴(Singleton Pattern) 처음부터 끝까지

오늘도 평화롭게 프리코스 주차 미션을 하고있던 와중.. 코드리뷰에서 다른 참가자가 작성한 코드리뷰가 떠올랐다. 이건 싱글톤 패턴을 사용하기 적합한 클래스네요! 싱글톤에 대해 아무것도

skianything.tistory.com

https://ittrue.tistory.com/558

 

[Java] 데코레이터 패턴(Decorator Pattern)이란? - 개념 및 예제

데코레이터 패턴(Decorator Pattern) 데코레이터 패턴(Decorator Pattern)은 객체 지향 디자인 패턴 중 하나로 객체에 동적으로 기능을 추가하여 확장할 수 있는 구조 패턴이다. 이 패턴은 상속을 통해 클

ittrue.tistory.com

https://mozartnt.tistory.com/69

 

C# [디자인 패턴] - 옵저버 패턴(Observer Pattern)

옵저버 패턴(Observer Pattern)이란?  옵저버 패턴(Observer Pattern)은 객체 간의 일대다 관계를 정의하여, 하나의 객체(주체, Subject)의 상태가 변경될 때, 이 상태를 관찰하는 여러 객체(옵저버, Observer)

mozartnt.tistory.com


Code Kata

오늘부터 이제 9-10시에 코드 카타를 진행한다.

프로그래머스에서 매일 문제 풀기!!

https://chromewebstore.google.com/detail/%EB%B0%B1%EC%A4%80%ED%97%88%EB%B8%8Cbaekjoonhub/ccammcjdkpgjmcpijpahlehmapgmphmk?pli=1

 

백준허브(BaekjoonHub) - Chrome 웹 스토어

Automatically integrate your BOJ submissions to GitHub

chromewebstore.google.com

백준허브를 사용하면 자동으로 github에 백업을 해준다.

와우..

 

내가 백준을 5달동안 매일 풀었는데 백준허브라는 이런 꿀 기능을 아무도 안알려줬다니...🫠

 

 

아무튼 세상이 참 편하고 좋네요..👍

 

 

https://github.com/yoonseo4343/codeKata

다시 앞으로 열심히 코테 준비를 해보겟서요!

 

'내배캠Unreal_TIL > C++' 카테고리의 다른 글

[TIL] 2025-12-29 | C++에서 중요한 건 메모리, 수명, 비용  (0) 2025.12.29
[TIL] 2025-12-23 | C++ 하면서 사소한 것들  (0) 2025.12.23
[TIL] 2025-12-19 | 과제4 구현  (0) 2025.12.19
[C++] STL 총정리  (0) 2025.12.18
[TIL] 2025-12-18 | 과제3 구현 및 vector 동작 원리 이해  (0) 2025.12.18
'내배캠Unreal_TIL/C++' 카테고리의 다른 글
  • [TIL] 2025-12-29 | C++에서 중요한 건 메모리, 수명, 비용
  • [TIL] 2025-12-23 | C++ 하면서 사소한 것들
  • [TIL] 2025-12-19 | 과제4 구현
  • [C++] STL 총정리
윤윤씨
윤윤씨
🎮 내일배움캠프 Unreal 7기
  • 윤윤씨
    컴퓨터온열맛사지
    윤윤씨
  • 전체
    오늘
    어제
    • 분류 전체보기 (62)
      • 내배캠Unreal_TIL (62)
        • C++ (23)
        • UE (31)
        • 팀프로젝트 (7)
      • etc (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
    • Solved.ac
    • YouTube
  • 태그

    오토마타
    디자인패턴
    머티리얼
    STL
    프로그래머스
    ta
    언리얼과제
    gas
    챌린지
    스테이트머신
    코드카타
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.6
윤윤씨
[TIL] 2025-12-22 | C++ 디자인패턴 - 싱글톤, 데코레이터, 옵저버 정리
상단으로

티스토리툴바