본문 바로가기
Unreal/[서적] 언리얼5 이득우님 인프런1

Ch 09. 델리게이트

by GameStudy 2023. 5. 12.

9.1 느슨한 결합

9.1-1 강한 결합과 느슨한 결합 

  - 강한 결합(Tight Coupling)

    클래스들이 서로 의존성을 가지는 경우.

    ex. Card 클래스가 없다면 Person 클래스가 만들어 질 수 없는 경우

      Person 클래스는 Card 클래스에 대한 의존성을 가진다고 함.

  - 느슨한 결합(Loose Coupling)

    실물에 의존하지 말고 추상적 설계에 의존하라.(DIP 원칙)

    ex. Person은 왜 Card가 필요한가? 출입을 확인해야 하기 때문.

      출입에 관련된 추상적인 설계를 만들어보자.

      ICheck를 상속 받은 새로운 카드 인터페이스를 통해 해결하고자 함.

      나중에 새로운 Card 클래스가 생성되도, ICheck 인터페이스를 상속 받는 방식.

  - 이러한 느슨한 결합 구조는 유지 보수를 손쉽게 만들어줌.

    다만, ICheck와 의존성이 떨어지다보니 그곳에서 무슨일이 벌어지는지는 모름.

 

9.1-2 느슨한 결합의 간편한 구현 - 델리게이트

  - 델리게이트는 함수를 오브젝트처럼 관리하는 방식.

  - 함수 포인터를 활용한 콜백 함수 방식

    가능 하지만 이를 정의하고 사용하는 과정이 꽤나 복잡함.

    안정성을 스스로 검증해야하고 C++ 17 규약의 std::bind와 std::function은 느림.

  - C#의 델리게이트 키워드의 모방

    함수를 마치 개체처럼 다룰 수 있음. 안정적이고 간편하게 선언 가능.

    언리얼도 느슨한 결합 구조를 위해 델리게이트를 지원함.

 

9.1-3 발행 구독 디자인 패턴

  - 푸시 형태의 알림을 구현하는데 적합한 디자인 패턴

  - 발행자와 구독자로 구분됨.

    제작자는 컨텐츠를 생산함.

    발행자는 컨텐츠를 배포함.

    구독자는 배포된 컨텐츠를 받아서 소비함.

    제작자와 구독자는 서로를 몰라도 발행자를 통해 컨텐츠를 생산하고 소비할 수 있음.(느슨한 결합)

  - 발행 구독 디자인 패턴의 장점

    제작자와 구독자는 서로를 모르기 때문에 느슨한 결합으로 구성됨.

    유지 보수가 쉽고 유연하게 활용될 수 있으며 테스트가 쉬워짐.

    시스템 스케일을 유연하게 조절할 수 있으며 기능 확장이 용이해짐.

  - 언리얼 엔진은 발행 구독 디자인 패턴을 위해 델리게이트 기능을 제공함.

    델리게이트의 사전적 의미는 대리자. 즉 위에서 설명한 발행자.

    배포는 Broadcast(), 구독은 Add() 함수로 가능함.

 

9.1-3 언리얼 델리게이트 선언시 고려사항

  - 어떤 데이터를 전달하고 받을 것인가

    매개변수 자료형과 그 갯수

    일대일 전달 / 일대다 전달

  - 프로그래밍 환경

    C++ 프로그래밍에서만 사용 / C++ 프로그래밍과 블루프린트 프로그래밍 사용

  - 어떤 함수와 연결할 것인가

    클래스 외부에 설계된 C++ 함수와 연결

    전역에 설계된 정적 함수와 연결

    언리얼 오브젝트의 멤버 함수와 연결(대부분 이 방식)

 

9.1-4 언리얼 델리게이트 선언 매크로

DECLARE_{델리게이트유형}_DELEGATE_{함수정보}

  - 델리게이트 유형

일대일 + C++ DECLARE_DELEGATE
일대다 + C++ DECLARE_MULTICAST
일대일 + C++ & 블루프린트 DECLARE_DYNAMIC
일대다 + C++ & 블루프린트 DECLARE_DYNAMIC_MULTICAST

  - 함수 정보

인자 없음 + 반환값 없음 공란
(ex. DECLARE_DELEGATE)
인자 한개 + 반환값 없음 OneParam
(ex. DECLARE_DELEGATE_OneParam)
인자 세개 + 반환값 있음
(인자는 최대 9개까지 지원함.)
RetVal_ThreeParams
(ex. DECLARE_DELEGATE_RetVal_ThreeParams)

 

9.1-5 언리얼 델리게이트 예제

  - 학사 정보(CourceInfo)와 학생(Student)가 있음. 시스템에서 학사 정보를 변경하면

    알림 구독한 학생들에게 변경 내용을 자동으로 전달하고자 함.

  - 새 C++ 클래스 > UObject 부모 클래스 > "CourseInfo" 클래스 생성

<hide/>

// CourseInfo.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "CourseInfo.generated.h"

DECLARE_MULTICAST_DELEGATE_TwoParams(FCourseInfoOnChangedSignature, const FString&, const FString&);

UCLASS()
class UNREALDELEGATE_API UCourseInfo : public UObject
{
	GENERATED_BODY()
	
public:
	UCourseInfo();

	FCourseInfoOnChangedSignature OnChanged;

	void ChangeCourseInfo(const FString& InSchoolName, const FString& InNewContents);

private:
	FString Contents;
	
};
<hide/>

// CourseInfo.cpp

#include "CourseInfo.h"

UCourseInfo::UCourseInfo()
{
	Contents = TEXT("기존 학사 정보");
}

void UCourseInfo::ChangeCourseInfo(const FString& InSchoolName, const FString& InNewContents)
{
	Contents = InNewContents;

	UE_LOG(LogTemp, Log, TEXT("[CourseInfo] 학사 정보가 변경되어 알림을 발송합니다."));
	OnChanged.Broadcast(InSchoolName, Contents);
}
<hide/>

// Student.h

...
class UNREALDELEGATE_API UStudent : public UPerson, public ILessonInterface
{
	...
    
	void GetNotification(const FString& School, const FString& NewCourseInfo);
};
<hide/>

// Student.cpp

...

void UStudent::GetNotification(const FString& School, const FString& NewCourseInfo)
{
	UE_LOG(LogTemp, Log, TEXT("[Student] %s님이 %s로부터 받은 메시지 : %s"), *Name, *School, *NewCourseInfo);
}
<hide/>

// MyGameInstance.h

...
class UNREALDELEGATE_API UMyGameInstance : public UGameInstance
{
	...

private:
	UPROPERTY()
	TObjectPtr<class UCourseInfo> CourseInfo;

	...
	
};
<hide/>

// MyGameInstance.cpp


...
#include "CourseInfo.h"

...

void UMyGameInstance::Init()
{
	Super::Init();

	CourseInfo = NewObject<UCourseInfo>(this);

	UE_LOG(LogTemp, Log, TEXT("============================"));

	UStudent* Student1 = NewObject<UStudent>();
	Student1->SetName(TEXT("학생1"));
	UStudent* Student2 = NewObject<UStudent>();
	Student2->SetName(TEXT("학생2"));
	UStudent* Student3 = NewObject<UStudent>();
	Student3->SetName(TEXT("학생3"));

	CourseInfo->OnChanged.AddUObject(Student1, &UStudent::GetNotification);
	CourseInfo->OnChanged.AddUObject(Student2, &UStudent::GetNotification);
	CourseInfo->OnChanged.AddUObject(Student3, &UStudent::GetNotification);

	CourseInfo->ChangeCourseInfo(SchoolName, TEXT("변경된 학사 정보"));

	UE_LOG(LogTemp, Log, TEXT("============================"));
}

 

'Unreal > [서적] 언리얼5 이득우님 인프런1' 카테고리의 다른 글

Ch 11. 구조체와 Map  (0) 2023.05.13
Ch 10. Array and Set  (0) 2023.05.12
Ch 08. 컴포지션  (0) 2023.05.11
Ch 07. 언리얼 인터페이스  (0) 2023.05.11
Ch 06. 언리얼 리플렉션 2  (0) 2023.05.10

댓글