Observer Pattern

Yongs12 ㅣ 2024. 2. 5. 09:53

 

Observer 예시 (인터페이스 정의)

#pragma once

/*=================================
		Observer Pattern
=================================*/

/*
객체의 상태 변화에 따라 다른 객체가 자동으로 업데이트되는 디자인 패턴으로
이벤트 시스템이나 데이터 변경 통지 시스템에서 사용 된다.

장점
ㄴ 느슨한 결합 : 옵저버 관리 객체와 옵저버 간의 결합도가 낮아 서로의 구현에 의존하지 않고 독립적으로 개발 가능
ㄴ 유연성 : 새로운 옵저버를 추가하거나 기존 옵저버를 제거하는 것이 용이하며 시스템의 변경이 비교적 쉽다.
ㄴ 재사용성 : 다양한 옵저버 관리 객체와 옵저버 조합을 재사용하기가 쉽다.

단점
ㄴ 복잡성 : 옵저버가 많을 수록 여러 옵저버에 대한 상태를 관리해야 하므로 복잡해질 수 있다.
ㄴ 메모리 누수 : 옵저버가 관리 객체에 등록된 후 관리 객체가 사라질 경우 옵저버가 여전히 참조를 유지할 가능성이 있다.

*/

enum class EEventType
{
	// 날씨 관련 이벤트
	DAY,  
	NIGHT,
	RAINY,
	SNOWY,

	// 다른 이벤트 추가
};

// 옵저버 인터페이스
class IObserver
{
public:
	virtual ~IObserver() {}

public:
	virtual void update(EEventType event) = 0;
};

// 관리 대상 인터페이스
class ISubject
{
public:
	virtual ~ISubject() {}

public:
	virtual void attach(IObserver* observer) = 0;  // 옵저버 등록
	virtual void detach() = 0;					   // 옵저버 해제
	virtual void notify(EEventType event) = 0;	   // 이벤트 통지
};

 

 

관리 클래스 생성 (Weather)

#pragma once
#include "Observer.h"

class WeatherSubject : public ISubject
{
public:
	WeatherSubject() {}
	~WeatherSubject() {}

public:
	void attach(IObserver* observer) override
	{
		_observers.push_back(move(observer));
	}

	void setWeather(EEventType event)
	{
		notify(event);
	}

private:
	void detach() override
	{
		_observers.clear();
	}
	
	void notify(EEventType event) override
	{
		for (IObserver* observer : _observers)
		{
			observer->update(event);
		}

		detach();
	}

private:
	vector<IObserver*> _observers;
};

 

 

 

Observer 인터페이스 상속

ㄴ Prototype Pattern에서 만든 Character 클래스 재사용

https://mygameprogramming.tistory.com/338

 

Prototype Pattern

Prototype 예시 (Character)#pragma once/*================================= Prototype Pattern=================================*//*프로토 타입 패턴은 객체 생성 방식 중 하나로 기존의 객체를 복제하여 새로운 객체를 만드는

mygameprogramming.tistory.com

 

 

/*
Observer Pattern을 위한 IObserver 상속 추가
*/
#include "Observer.h"

class Character : public IObserver
{
public:
    virtual ~Character() {}

protected:
    Character() {}
    Character(int hp, int mp, int speed, string name)
        : _hp(hp), _mp(mp), _speed(speed), _name(name)
    {}

public:
    int getHP() const 
    { 
        return _hp; 
    }
    int getMP() const 
    { 
        return _mp; 
    }
    int getSpeed() const 
    { 
        return _speed; 
    }

    string getName() const
    {
        return _name;
    }

    void setHP(int health) 
    { 
        _hp = health; 
    }
    void setMP(int magic) 
    { 
        _mp = magic; 
    }
    void setSpeed(int spd) 
    { 
        _speed = spd; 
    }

    void setName(const string& name)
    {
        _name = name;
    }

public:
    virtual unique_ptr<Character> clone() const = 0;
    virtual void display() const = 0;
    virtual void attack() = 0;

public:
    void update(EEventType event) override
    {
        switch (event)
        {
            case EEventType::DAY:
                dayUpdate();
            break;
            case EEventType::NIGHT:
                nightUpdate();
            break;
        }
    }

private:
    void dayUpdate()
    {
        _speed += 5;
        if (_speed > 100)
        {
            _speed = 100;
        }

        cout << _name << " " << "현재 이동 속도 : " << _speed << " " << "낮일 때 이동속도 증가\n";
    }
    void nightUpdate()
    {
        _speed -= 10;
        if (_speed < 0)
        {
            _speed = 0;
        }

        cout << _name << " " << "현재 이동 속도 : " << _speed << " " << "밤일 때 이동속도 감소\n";
    }

private:
    string _name;
    int _hp = 0;    
    int _mp = 0;    
    int _speed = 0; 
};

 

 

Observer 사용

enum Hour
{
	DAY,
	NIGHT
};

// 임시 시간 구하기
int getHour() 
{
	auto now = chrono::system_clock::now();
	time_t now_c = chrono::system_clock::to_time_t(now);

	tm localTime;
	localtime_s(&localTime, &now_c);

	int hour = localTime.tm_hour;

    if (hour >= 6 && hour < 17) 
	{
        return DAY;
    }
    else 
	{
        return NIGHT;
    }
}

int main()
{
	Mage prototypeMage(100, 100, 80, "메이지");
	Warrior prototypeWarrior(200, 50, 70, "워리어");

	unique_ptr<Character> copyMage = prototypeMage.clone();
	unique_ptr<Character> copyWarrior = prototypeWarrior.clone();

	WeatherSubject weatherSubject;

	weatherSubject.attach(copyMage.get());
	weatherSubject.attach(copyWarrior.get());

	copyMage->attack();

	if (getHour() == DAY)
	{
		weatherSubject.setWeather(EEventType::DAY);
	}
	else if(getHour() == NIGHT)
	{
		weatherSubject.setWeather(EEventType::NIGHT);
	}

	return 0;
}

 

결과 출력

'Design Pattern' 카테고리의 다른 글

Stategy pattern  (0) 2024.02.07
State Pattern  (0) 2024.02.06
Prototype Pattern  (0) 2024.02.04
Builder Pattern  (0) 2024.02.03
Factory Pattern  (0) 2024.02.02