C++
[강의] 1월 2일 수업정리
k-codestudy
2025. 1. 2. 18:11
오늘은 옵저버 패턴에 대한 수업을 들었다.
1. 옵저버 패턴
1.1 정의
옵저버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다.
1.2 특징
1. 주제가 옵저버에 대해서 아는것은 옵저버가 특정 인터페이스를 구현한다는 것 뿐이다.
2. 옵저버는 언제든지 새로 추가할수있다
3. 새로운 형식의 옵저버를 추가하려고 할때도 주제를 전혀 변경할 필요가 없다
4. 주제와 옵저버는 서로 독립적으로 재사용할수있다.
5. 주제나 옵저버가 바뀌더라도 서로한테 영향을 미치지 않는다.
즉, 여기서 핵심은 내용이 변경되었을 때 알려준다 이며 보통 게임쪽에서 UI에서 비슷하게 사용이 된다.
1.3 느슨한 결합
옵저버 패턴에 느슨한 결합이라는 개념이 들어가게 되는데 객체들이 서로 결합이 되어 있긴 하지만 강한 결합 상태 보다는 느슨한 상태의 결합이라고 볼 수 있다. 어떤 클래스에는 다른 클래스를 직접적으로 사용하는 클래스 의존성을 줄인 결합 상태를 말한다.
즉, 컴퍼넌트 간의 내부 의존성을 줄이는 것을 추구하는 디자인 목표이다.
1.4 UML
1.5 구현
[ weather.h / cpp ]
#pragma once
#include <stdio.h>
#include <set>
class C_OBSERVER;
__interface I_SUBJECT
{
void registerObserver(C_OBSERVER* pObserver);
void removeObserver(C_OBSERVER* pObserver);
void notifyObserver();
};
class C_SUBJECT abstract : public I_SUBJECT
{
protected:
std::set<C_OBSERVER*> m_setObservers;
public:
C_SUBJECT() = default;
virtual ~C_SUBJECT() = default;
void registerObserver(C_OBSERVER* pObserver) override;
void removeObserver(C_OBSERVER* pObserver) override;
};
class C_WEATHER : public C_SUBJECT
{
private:
float m_fTemperature;
float m_fHumidity;
float m_fPressure;
public:
C_WEATHER() = default;
~C_WEATHER() = default;
void setMeasurements(float fTemperature, float fHumidity, float fPressure);
void notifyObserver() override;
};
===
#include "weather.h"
#include "observer.h"
void C_SUBJECT::registerObserver(C_OBSERVER * pObserver)
{
m_setObservers.insert(pObserver);
}
void C_SUBJECT::removeObserver(C_OBSERVER* pObserver)
{
m_setObservers.erase(pObserver);
}
void C_WEATHER::notifyObserver()
{
for (C_OBSERVER* pObserver : m_setObservers) // 이터레이터의 비긴부터 앤드까지의 개념
{
pObserver->updata(m_fTemperature, m_fHumidity, m_fPressure);
}
}
void C_WEATHER::setMeasurements(float fTemperature, float fHumidity, float fPressure)
{
m_fHumidity = fHumidity;
m_fPressure = fPressure;
m_fTemperature = fTemperature;
notifyObserver();
}
1.5.1 클래스 설명
- I_SUBJECT (인터페이스)
- 주체가 반드시 구현해야 할 메서드를 정의
- registerObserver, removeObserver, notifyObserver를 선언
- C_SUBJECT (추상 클래스)
- I_SUBJECT를 상속받아 기본적인 주체의 기능을 구현한 클래스
- 관찰자(C_OBSERVER)를 관리하기 위한 std::set<C_OBSERVER*> m_setObservers를 멤버로 가진다
- 관찰자를 추가/제거하는 메서드를 구현.
- C_WEATHER (구체적인 주체)
- C_SUBJECT를 상속받아 날씨 데이터를 관리하는 역할
- 날씨 정보(온도, 습도, 기압)를 멤버 변수로 가지며, setMeasurements를 통해 데이터를 변경하고, 변경 시 notifyObserver를 호출하여 관찰자들에게 알린다
1.5.2 메서드 설명
- registerObserver
- 관찰자를 주체의 리스트(m_setObservers)에 추가
- removeObserver
- 관찰자를 주체의 리스트에서 제거
- notifyObserver
- 주체가 가진 모든 관찰자에게 상태 변경을 알린다
- for 루프를 사용하여 관찰자 리스트를 순회하면서 updata를 호출
- setMeasurements
- 날씨 데이터를 업데이트하는 메서드
- 데이터를 변경한 후, notifyObserver를 호출하여 변경 사항을 알린다
[ observer.h / cpp ]
#pragma once
#include <stdio.h>
#include "weather.h"
__interface I_OBSERVER
{
void updata(float fTemperature, float fHumidity, float fPressure);
};
class C_OBSERVER abstract : public I_OBSERVER
{
private:
C_SUBJECT* m_pSubject;
public:
C_OBSERVER(C_SUBJECT* pSubject);
virtual ~C_OBSERVER() = default;
};
class C_CURRENT_DISPLAY : public C_OBSERVER
{
public:
C_CURRENT_DISPLAY(C_SUBJECT* pSubject);
void updata(float fTemperature, float fHumidity, float fPressure) override;
};
class C_TEXT_DISPLAY : public C_OBSERVER
{
public:
C_TEXT_DISPLAY(C_SUBJECT* pSubject);
void updata(float fTemperature, float fHumidity, float fPressure) override;
};
===
#include "observer.h"
C_OBSERVER::C_OBSERVER(C_SUBJECT* pSubject) :
m_pSubject{}
{
m_pSubject = pSubject;
m_pSubject->registerObserver(this);
}
C_CURRENT_DISPLAY::C_CURRENT_DISPLAY(C_SUBJECT* pSubject) :
C_OBSERVER(pSubject)
{
}
void C_CURRENT_DISPLAY::updata(float fTemperature, float fHumidity, float fPressure)
{
printf("메인 디스플레이 : %f, %f, %f\n", fTemperature, fHumidity, fPressure);
}
C_TEXT_DISPLAY::C_TEXT_DISPLAY(C_SUBJECT* pSubject) :
C_OBSERVER(pSubject)
{
}
void C_TEXT_DISPLAY::updata(float fTemperature, float fHumidity, float fPressure)
{
printf("text : %f, %f, %f\n", fTemperature, fHumidity, fPressure);
}
1.5.3 클래스 설명
- I_OBSERVER (인터페이스)
- 관찰자가 구현해야 할 메서드를 정의
- updata 메서드를 선언
- C_OBSERVER (추상 클래스)
- I_OBSERVER를 상속받아 관찰자의 기본 구조를 제공
- 관찰자는 주체(C_SUBJECT)와 연결되며, 생성자에서 주체에 자신을 등록
- C_CURRENT_DISPLAY와 C_TEXT_DISPLAY (구체적인 관찰자)
- C_OBSERVER를 상속받아 날씨 데이터를 출력하는 역할
- updata 메서드를 구현하여 각자의 방식으로 데이터를 처리
1.5.4 메서드 설명
- updata
- 관찰자가 주체로부터 전달받은 데이터를 처리
- C_CURRENT_DISPLAY는 "메인 디스플레이", C_TEXT_DISPLAY는 "text" 형식으로 데이터를 출력
[ main.cpp ]
#include <iostream>
#include "weather.h"
#include "observer.h"
int main()
{
C_WEATHER cWeather{};
C_CURRENT_DISPLAY cDisplay1(&cWeather);
C_CURRENT_DISPLAY cDisplay2(&cWeather);
C_TEXT_DISPLAY cDisplay3(&cWeather);
cWeather.setMeasurements(10.f, 20.f, 30.f);
cWeather.removeObserver(&cDisplay2);
cWeather.setMeasurements(5.f, 10.f, 15.f);
cWeather.setMeasurements(1.f, 2.f, 3.f);
}