서론

  • 최근에 언리얼 엔진 액터 컴포넌트 쪽 분석을 했는데 팀원 중 한 분이 액터 컴포넌트 메서드 중 PhysicCreate 하는 부분에 대해서 오버라이딩을 할 함수를 호출하는 것과 멀티캐스트 델리게이트를 호출하는 것에 대한 차이점에 의문을 품었다.

 

구조

  • PhysicsCreate라는 메서드가 있을 때 그 메서드 내에서 OnPhysicsCreate 라는 메서드를 호출하고 그 다음 줄에서 PhysicsCreate.Broadcast 델리게이트를 전파하는 구조
  • 비슷한 처리를 하는건데 왜 오버라이딩 버전과 델리게이트 버전으로 2개를 구분해놨는가 ?

 

예제

  • 친근한 예로 유튜브에 어떤 동영상이 올라오면 구독한 사람에 한해서 알람이 가는 경우를 생각해봤음
  • Youtuber의 Upload 메서드 내에서 OnUpload ( 오버라이딩 버전 ) 과 UploadDelegate 들을 호출하는 ( C++에서 흉내낸 멀티캐스트 델리게이트 ) 것의 차이를 구분해 봄
  • 결론적으로 OnUpload를 오버라이딩한 함수의 경우, 동영상의 품질 설정이나 동영상의 유료 컨테츠를 쓰겠는가 하는 부분에 대한 처리를 맡았고, UploadDelegate는 구독자들이 구독을 하게 된 경우 동영상이 올라가는 시점에 구독자들에게 알람이 갈 수 있게 하고 구독자마다 그 시점의 동작을 정의할 수 있게 했음
  • 오버라이드 버전은 동영상에 대한 처리, 멀티캐스트 델리게이트는 구독자에 대한 처리를 할 수 있었음
  • 언리얼 엔진의 델리게이트의 경우 디자인 패턴 중 구독 - 발행 (Observer) 패턴을 모티브로 만들었으며 구독 - 발행 패턴이 가지는 가장 큰 이점은 불특정 다수에 대한 등록의 제한이 없다는 것이다. 아래 main 에서도 볼 수 있듯이 subscriber는 본인이 구독하고 싶은 youtuber에게 subscribe를 하게 되면 유튜버는 동영상이 올라가는 시점에 모든 subscriber에게 알람을 보내줄 수 있게 된다.
#include <iostream>
#include <vector>
#include <string>
#include <functional>

class Youtuber
{
public:
	using OnUploadDelegate  = std::function<void(Youtuber*)>;

	Youtuber(std::string name) : name(name) {}

public:
	void Upload()
	{
		//업로드에 대한 전반적인 처리, 동영상 품질 및 유료 시스템 결제 등
		//또한 UploadDelegate(Broadcast)들에 대해 호출(Notify)하기 전에 본인의 값 세팅이 완료될 수 있게끔 순서를 보장하기도 함
		OnUpload();

		for (auto& Upload : UploadDelegate)
		{
			Upload(this);
		}
	}

protected:
	virtual void OnUpload() {}

public:
	void AddSubscriber(OnUploadDelegate fn)
	{
		UploadDelegate.push_back(fn);
	}

public:
	std::string GetName() { return name; }

private:
	std::vector<OnUploadDelegate > UploadDelegate;
	std::string name;
};

class Youtuber_Woojin : public Youtuber
{
public:
	Youtuber_Woojin(std::string name) : Youtuber(name) {}

protected:
	virtual void OnUpload() override
	{
		std::cout << "동영상 품질을 1080p로 올렸습니다!!" << std::endl;
		std::cout << "동영상을 프리미엄으로 등록합니다!!" << std::endl;
	}
};

class Subscriber
{
public:
	Subscriber(std::string name) : name(name) {}

	void SubScribe(Youtuber* youtuber)
	{
		youtuber->AddSubscriber(GetNotify());
	}

protected:
	virtual Youtuber::OnUploadDelegate GetNotify()
	{
		return [](Youtuber* youtuber) {
			std::cout << youtuber->GetName() << "님꼐서 새 동영상을 업로드하였습니다!" << std::endl;
		};
	}

public:
	std::string GetName() { return name; }

private:
	std::string name;
};

class Subscriber1 : public Subscriber
{
public:
	using Subscriber::Subscriber;

protected:
	virtual Youtuber::OnUploadDelegate GetNotify() override
	{
		return [](Youtuber* youtuber) {
			std::cout << youtuber->GetName() << "님께서 새 동영상을 업로드하였습니다" << std::endl;
			std::cout << "동영상을 보지 않습니다!" << std::endl;
		};
	}
};

class Subscriber2 : public Subscriber
{
public:
	using Subscriber::Subscriber;

protected:
	virtual Youtuber::OnUploadDelegate GetNotify() override
	{
		return std::bind(&Subscriber2::Notify_Internal, this, std::placeholders::_1);
	}

private:
	void Notify_Internal(Youtuber* youtuber)
	{
		std::cout << youtuber->GetName() << "님께서 새 동영상을 업로드하였습니다" << std::endl;
		std::cout << GetName() << "님은 메시지를 무시했습니다!" << std::endl;
	}
};

int main()
{
	Youtuber* big_youtuber = new Youtuber_Woojin("woojin");
	Subscriber* subscriber1 = new Subscriber("subscriber1");
	Subscriber* subscriber2 = new Subscriber1("subscriber2");
	Subscriber* subscriber3 = new Subscriber2("subscriber3");

	subscriber1->SubScribe(big_youtuber);
	subscriber2->SubScribe(big_youtuber);
	subscriber3->SubScribe(big_youtuber);

	big_youtuber->Upload();

	delete subscriber3;
	delete subscriber2;
	delete subscriber1;
	delete big_youtuber;

	return 0;
}

 

깃허브

https://github.com/shimwoojin/WoojinCpp/blob/master/Describe/FromUnrealEngine/OnCreateAndBroadcast.cpp

 

+ Recent posts