24.3. 프로그램언어 C++에서의 옵저버 패턴

프로그램언어 C++의 옵저버 패턴 개념

옵저버 패턴은 객체 간의 일대다 의존 관계를 정의하는 디자인 패턴입니다. 이 패턴은 주체(Subject)와 여러 개의 옵저버(Observer)로 구성되어 있습니다. 주체는 상태가 변경되면 등록된 모든 옵저버에게 알림을 보내고, 옵저버는 이를 받아서 상태에 따라 적절한 동작을 수행합니다.

옵저버 패턴은 C++에서도 많이 활용되며, 예를 들어 GUI 프로그래밍에서 이벤트 처리나 데이터 변경 감지 등에 사용됩니다. 아래는 간단한 C++ 예제 코드를 통해 옵저버 패턴을 이해해보겠습니다.


#include <iostream>
#include <vector>

class Observer {
public:
    virtual void update(int data) = 0;
};

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

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

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

    void notify() {
        for (Observer* observer : observers) {
            observer->update(state);
        }
    }
};

class ConcreteObserver : public Observer {
public:
    void update(int data) override {
        std::cout << "Received data: " << data << std::endl;
    }
};

int main() {
    Subject subject;
    ConcreteObserver observer1, observer2;

    subject.attach(&observer1);
    subject.attach(&observer2);

    subject.setState(100);

    return 0;
}

위 예제 코드에서 Subject는 주체를, Observer는 옵저버를 나타냅니다. Subject는 attach 메서드를 통해 옵저버를 등록하고, setState 메서드를 통해 상태를 변경하면서 등록된 옵저버들에게 알림을 보냅니다. ConcreteObserver는 실제로 알림을 받아서 동작을 수행하는 구체적인 옵저버를 나타냅니다.

프로그램언어 C++에서의 옵저버 패턴 적용법

옵저버 패턴은 객체의 상태 변화를 관찰하는 다른 객체들에게 알림을 전달하는 디자인 패턴입니다. C++에서 옵저버 패턴을 적용하기 위해서는 주제(Subject)와 옵저버(Observer) 인터페이스를 정의하고, 이를 구현하는 클래스들을 만들어야 합니다.

옵저버 패턴을 C++로 구현하는 방법은 다음과 같습니다. 먼저, Subject 클래스와 Observer 클래스를 정의합니다. Subject 클래스에는 옵저버들을 등록하고 알림을 보내는 메서드가 있어야 합니다. Observer 클래스에는 상태 변화를 감지하고 처리하는 가상 메서드가 있어야 합니다.

다음으로, Subject 클래스를 상속받는 구체적인 주제 클래스를 만들고, Observer 클래스를 상속받는 구체적인 옵저버 클래스를 만듭니다. 주제 클래스에서는 옵저버들을 등록하고 알림을 보내는 로직을 구현하고, 옵저버 클래스에서는 상태 변화를 감지하고 처리하는 로직을 구현합니다.

옵저버 패턴을 C++로 구현한 예제 코드는 아래와 같습니다.


#include <iostream>
#include <vector>

class Observer {
public:
    virtual void update(int data) = 0;
};

class Subject {
private:
    std::vector<Observer*> observers;

public:
    void addObserver(Observer* observer) {
        observers.push_back(observer);
    }

    void notifyAll(int data) {
        for (Observer* observer : observers) {
            observer->update(data);
        }
    }
};

class ConcreteObserver : public Observer {
public:
    void update(int data) override {
        std::cout << "Received data: " << data << std::endl;
    }
};

class ConcreteSubject : public Subject {
public:
    void dataChanged(int newData) {
        notifyAll(newData);
    }
};

int main() {
    ConcreteObserver observer1;
    ConcreteObserver observer2;

    ConcreteSubject subject;
    subject.addObserver(&observer1);
    subject.addObserver(&observer2);

    subject.dataChanged(10);

    return 0;
}

프로그램언어 C++의 옵저버 패턴 사용 사례

옵저버 패턴은 C++ 프로그래밍에서 많이 사용되는 디자인 패턴 중 하나입니다. 이 패턴은 객체 간의 일대다 의존성을 정의하여, 어떤 객체의 상태가 변할 때 그 객체에 의존하는 다른 객체들이 자동으로 알림을 받고 업데이트될 수 있도록 합니다.

옵저버 패턴을 사용하는 사례 중 하나는 주식 시장 시뮬레이션 프로그램입니다. 주식 시장에서 주식의 가격이 변동될 때, 주식을 관찰하는 투자자들에게 즉시 알림을 주어야 합니다. 이때 옵저버 패턴을 사용하여 주식 객체가 변동될 때마다 투자자 객체들에게 자동으로 알림을 전달할 수 있습니다.


#include <iostream>
#include <vector>

// 주식 시장을 관찰하는 옵저버 클래스
class Investor {
public:
    virtual void update(float price) = 0;
};

// 주식 시장을 모델링하는 주식 클래스
class Stock {
private:
    float price;
    std::vector<Investor*> investors;

public:
    void setPrice(float newPrice) {
        price = newPrice;
        notifyInvestors();
    }

    void attach(Investor* investor) {
        investors.push_back(investor);
    }

    void notifyInvestors() {
        for (Investor* investor : investors) {
            investor->update(price);
        }
    }
};

// 구체적인 투자자 클래스
class ConcreteInvestor : public Investor {
public:
    void update(float price) override {
        std::cout << "주식의 가격이 " << price << "로 변동되었습니다." << std::endl;
    }
};

int main() {
    Stock stock;
    ConcreteInvestor investor1;
    ConcreteInvestor investor2;

    stock.attach(&investor1);
    stock.attach(&investor2);

    stock.setPrice(100.0f);

    return 0;
}

프로그램언어 C++에서의 옵저버 패턴 장단점

옵저버 패턴은 C++ 프로그래밍에서 많이 사용되는 디자인 패턴 중 하나입니다. 이 패턴은 객체 간에 일대다 의존성을 정의하여, 어떤 객체의 상태가 변할 때 그 객체에 의존하는 다른 객체들이 자동으로 알림을 받고 상태 변화에 대응할 수 있도록 합니다.

옵저버 패턴의 장점은 다음과 같습니다:

  • 느슨한 결합: 주체 객체와 옵저버 객체들 간의 관계가 느슨하게 유지되어, 객체 간의 상호작용이 유연해집니다.
  • 확장성: 새로운 옵저버를 추가하거나 기존 옵저버를 제거하기 쉽습니다.
  • 재사용성: 주체 객체와 옵저버 객체들 간의 인터페이스를 통일하여, 재사용성이 높아집니다.

옵저버 패턴의 단점은 다음과 같습니다:

  • 메모리 누수: 주체 객체와 옵저버 객체들 간의 관계가 복잡해질 경우, 메모리 누수가 발생할 수 있습니다.
  • 성능 저하: 상태 변화가 발생할 때마다 모든 옵저버들에게 알림을 보내야 하므로, 성능 저하가 발생할 수 있습니다.

이제 C++에서의 옵저버 패턴을 예제 코드를 통해 살펴보겠습니다:


#include <iostream>
#include <vector>

class Observer {
public:
    virtual void update(int value) = 0;
};

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

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

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

    void notify() {
        for (Observer* observer : observers) {
            observer->update(state);
        }
    }
};

class ConcreteObserver : public Observer {
public:
    void update(int value) override {
        std::cout << "Received update with value: " << value << std::endl;
    }
};

int main() {
    Subject subject;
    ConcreteObserver observer1;
    ConcreteObserver observer2;

    subject.attach(&observer1);
    subject.attach(&observer2);

    subject.setState(10);

    return 0;
}

위의 예제 코드는 간단한 옵저버 패턴을 구현한 것입니다. Subject 클래스는 상태를 가지고 있고, 상태가 변경되면 옵저버들에게 알림을 보내는 역할을 합니다. ConcreteObserver 클래스는 Observer를 상속받아 update 함수를 구현하여 알림을 받는 역할을 합니다. main 함수에서 Subject에 옵저버를 추가하고 상태를 변경하면, 옵저버들이 알림을 받고 각자의 업데이트를 수행합니다.

프로그램언어 C++의 옵저버 패턴과 다른 디자인 패턴 비교

옵저버 패턴과 다른 디자인 패턴을 비교해보겠습니다.

옵저버 패턴

옵저버 패턴은 객체 간의 일대다 의존 관계를 정의하는 패턴으로, 어떤 객체의 상태가 변할 때 그 객체에 의존하는 다른 객체들이 자동으로 알림을 받고 상태에 대한 업데이트를 수행할 수 있도록 합니다.

옵저버 패턴 예제 코드


#include <iostream>
#include <vector>

class Observer {
public:
    virtual void update() = 0;
};

class Subject {
private:
    std::vector<Observer*> observers;

public:
    void addObserver(Observer* observer) {
        observers.push_back(observer);
    }

    void notifyObservers() {
        for (Observer* observer : observers) {
            observer->update();
        }
    }
};

class ConcreteObserver : public Observer {
public:
    void update() override {
        std::cout << "Received update from Subject" << std::endl;
    }
};

int main() {
    Subject subject;
    ConcreteObserver observer1, observer2;

    subject.addObserver(&observer1);
    subject.addObserver(&observer2);

    subject.notifyObservers();

    return 0;
}

다른 디자인 패턴

옵저버 패턴과 대조적으로 싱글톤 패턴은 어플리케이션 전반에 걸쳐 하나의 인스턴스만을 유지하고 이에 대한 전역적인 접근을 제공하는 패턴입니다. 싱글톤 패턴은 전역 상태를 유지하고 공유하는 데 유용하며, 객체 생성 및 관리에 특화되어 있습니다.

싱글톤 패턴 예제 코드


#include <iostream>

class Singleton {
private:
    static Singleton* instance;

    Singleton() {}

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

    void doSomething() {
        std::cout << "Singleton instance is doing something" << std::endl;
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* singleton1 = Singleton::getInstance();
    Singleton* singleton2 = Singleton::getInstance();

    singleton1->doSomething();
    singleton2->doSomething();

    return 0;
}

Leave a Comment