객체 지향 프로그래밍을 하다 보면, 클래스들간의 관계가 형성되는 경우가 있습니다.
이 경우를 클래스간의 결합이 생겼다고 표현하는데, 결합에는 2개의 구분이 있습니다.
1) 결합의 구분 - 강한 결합 VS 느슨한 결합
하나의 예시를 들어보겠습니다.
이전의 포스트에서 Weapon 클래스는 UndefinedPlayer 클래스의 컴포지션으로 사용되었습니다.
그런데, 만약 Weapon 클래스의 프로퍼티가 변경되었다고 가정한다면
UndefinedPlayer로부터 상속된 자식 클래스들의 컴포지션도 수정해줘야 할 것입니다.
이처럼 클래스들이 서로 의존성을 갖는 경우를 강한 결합이라고 표현합니다.
강한 결합이 유지된 클래스는 클래스들간의 관계로 인해 유지, 보수가 매우 어려워집니다.
이와는 반대로 인터페이스도 동작한 SummonInterface 클래스는
필요한 클래스들만이 상속받게끔 설계하였습니다.
따라서 SummonInterface에 변경사항이 생겼다면 상속받은 클래스들만 수정해주면 될 것입니다.
이와 같이 실물에 의존하지 않고 추상적 설계에 의존하는 결합을 느슨한 결합이라고 합니다.
느슨한 결합을 유지하는 클래스는 강한 결합과는 다르게 유지, 보수를 매우 쉽게 할 수 있습니다.
2) Delegate
이런 느슨한 결합 구조를 만들기 위해서는 함수를 오브젝트처럼 관리하는 것이 좋습니다.
그리고 이를 위해 언리얼 C++은 delegate를 지원합니다.
C++이후에 개발된 C# 역시 delegate 키워드를 지원합니다.
그러나 C#과는 다르게 언리얼에서는 발행-구독 디자인 패턴을 사용하여 delegate를 사용합니다.
3) 발행 - 구독 디자인 패턴
간단하게 그림으로 발행 - 구독 디자인 패턴을 알아보겠습니다.
발행 - 구독 디자인 패턴에서 클래스는 콘텐츠 제작자와 구독자로 구분됩니다.
그리고 델리게이트는 발행자의 역할을 수행합니다.
여기서 가장 중요한 것은
콘텐츠 제작자와 구독자는 서로를 몰라도, 발행자를 통해 콘텐츠를 전달하고 받을 수 있다는 것입니다.
예시로 들자면, HP 바와 같은 UI를 플레이어의 상태에 맞게 변형하는 경우에 델리게이트를 사용할 수 있습니다.
그리고 델리게이트를 통해 클래스 간의 느슨한 결합을 구현할 수 있습니다.
4) 언리얼 델리게이트 사용방법
언리얼 엔진에서는 델리게이트를 사용하기 위해 매크로를 제공합니다.
매크로는 다음과 같습니다.
DECLARE_{델리게이트 유형}_DELEGATE_{함수 정보}
매크로에 사용할 수 있는 인자는 다음의 예시처럼 상황에 맞게 사용하면 됩니다.
1) 델리게이트 유형 : 어떤 유형의 델리게이트인지 구상
(1) 1 : 1 형태로 C++만 지원한다면 유형은 공란으로 둠 : DECLARE_DELEGATE
(2) 1 : 다 형태로 C++만 지원한다면 유형은 MULTICAST : DECLARE_MULTICAST
(3) 1 : 1 형태로 블루프린트도 지원한다면 유형은 DYNAMIC : DECLARE_DYNAMIC
(4) 1 : 다 형태로 블루프린트도 지원한다면 유형은 DYNAMIC과 MULTICAST : DCLARE_DYNAMIC_MULTICAST
2) 함수 정보 : 연동될 함수의 형태를 지정
(1) 인자가 없고 반환값도 없는 함수라면 함수 정보는 공란 : DECLARE_DELEGATE
(2) 인자가 하나지만, 반환값이 없으면 함수 정보는 OneParam : DECLARE_DELGATE_OneParam
(3) 인자가 3개면서, 반환값이 존재한다면 함수유형은 RetVal_ThreeParams
: DECLARE_DELEGATE_RetVal_ThreeParams
추가적으로 멀티캐스트의 경우, 반환값을 지원하지 않습니다. 또한 함수의 인자는 최대 9개까지 지정가능합니다.
5) 델리게이트 예제
델리게이트를 사용할 예제는 위와 같습니다.
코드의 구성은 다음과 같습니다.
콘텐츠를 제공하는 쪽인 CharacterData 클래스입니다.
델리게이트 사용규칙을 명시한것과 같이 작성하였고 편의 상 헤더 파일에 몰아넣었습니다.
다음으로는 UndefinedPlayer 클래스입니다.
델리게이트로 발행된 콘텐츠를 받기 위해 GetNotification 함수를 만들었고
virtual 키워드를 사용하여 상속된 클래스만의 프로퍼티를 사용할 수 있게 하였습니다.
이어서 UndefinedPlayer 클래스로부터 상속된 클래스들입니다.
MyGameInstance 클래스에서는 델리게이트와 클래스를 묶어주었습니다.
그리고 최종적으로 델리게이트를 통해 발행하는 역할을 수행합니다.
최종 실행결과는 다음과 같습니다.
정리하면, 위의 예제의 CharacterData와 UndefinePlayer 클래스는 서로 헤더파일을 참조하지 않습니다.
그러나 델리게이트를 통해 느슨한 결합을 구현함으로써 클래스간의 관계를 맺는 데 성공하였습니다.
이와 같은 느슨한 결합은 클래스의 수가 무지막지하게 늘어나는 완성된 게임에서 특히 큰 도움을 줄 것입니다.
'언리얼 > 언리얼 C++' 카테고리의 다른 글
언리얼 C++ - 자료 구조 ~ TSet (0) | 2024.07.20 |
---|---|
언리얼 C++ - 자료 구조 ~ TArray (0) | 2024.07.20 |
언리얼 C++ - 컴포지션 (0) | 2024.07.07 |
언리얼 C++ - 인터페이스 (0) | 2024.06.30 |
언리얼 C++ - 언리얼 리플렉션 시스템의 활용 (0) | 2024.06.30 |