언리얼C++ 설계(2) - 컴포지션

myeongrangcoding·2023년 12월 1일

언리얼 C++ 설계(2) - 컴포지션

  • 언리얼 C++의 컴포지션 기법을 사용해 오브젝트 포함 관계 설계
  • 언리얼 C++이 제공하는 확장 열거형 타입의 선언과 활용

컴포지션

  • 객체 지향 설계에서 Has-A 관계를 구현하는 설계 방법
  • 복잡한 기능을 가진 거대한 클래스를 효과적으로 설계
  • 모던 객체 설계 기법에서는 컴포지션을 효과적으로 설계하는 것을 중요시함.

SOLID

  • 좋은 객체지향 설계 패턴을 제작하기 위한 모던 객체 설계 기법
    • Single Responsibility Principle(단일 책임 원칙)
      • 하나의 객체는 하나의 의무만 가지도록 설계
    • Open-Closed Principle(개방 폐쇄 원칙)
      • 기존에 구현된 코드를 변경하지 않으면서 새로운 기능을 추가할 수 있도록 설계
    • Liskov Substitution Principle(리스코프 치환 원칙)
      • 자식 객체를 부모 객체로 변경해도 작동에 문제 없을 정도로 상속을 단순히 사용
    • Interface Segregation Design(인터페이스 분리 원칙)
      • 객체가 구현해야 할 기능이 많다면 이들을 여러 개의 단순한 인터페이스들로 분리해 설계
    • Dependency Injection Principle(의존성 역전 원칙)
      • 구현된 실물보다 구축해야 할 추상적 개념에 의존
  • 모던 객체 설계 핵심은 상속을 단순화하고, 컴포지션을 적극 활용해서, 단순한 기능을 가진 다수의 객체를 조합해 복잡한 객체를 구성하는데 있음.

언리얼 엔진에서의 컴포지션 구현 방법

  • 방법 1: CDO에 미리 언리얼 오브젝트를 생성해 조합한다.(필수적 포함, CreateDefaultSubobject())
  • 방법 2: CDO에 빈 포인터만 넣고 런타임에서 언리얼 오브젝트를 생성해 조합한다.(선택적 포함, NewObject())
  • Subobject: 내가 소유한 언리얼 오브젝트.
  • Outer: 나를 소유한 언리얼 오브젝트.

UENUM()
enum class ECardType : uint8
{
	Student = 1 UMETA(DisplayName = "For Student"),
	Teacher UMETA(DisplayName = "For Teacher"),
	Staff UMETA(DisplayName = "For Staff"),
	Invalid
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameInstance.h"	// 해당 언리얼 오브젝트에 선언된 이 헤더가 가장 위쪽에 위치해 있어야 함.
#include "Student.h"
#include "Teacher.h"
#include "Staff.h"
#include "Card.h"

UMyGameInstance::UMyGameInstance()
{
	// 에디터가 활성화되기 이전에 초기화되는 순서를 가지고 있기 때문에
	// 이때 초기화를 진행해도 에디터에서 인지를 못하는 경우가 종종 있음.
	// 생성자 코드를 변경하게 되는 경우에는 에디터를 꺼줘야 됨.
	SchoolName = TEXT("기본학교");
}

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

	UE_LOG(LogTemp, Log, TEXT("============================"));
	TArray<UPerson*> Persons = { NewObject<UStudent>(), NewObject<UTeacher>(), NewObject<UStaff>() };
	for (const auto Person : Persons)
	{
		const UCard* OwnCard = Person->GetCard();
		check(OwnCard);
		ECardType CardType = OwnCard->GetCardType();
		UE_LOG(LogTemp, Log, TEXT("%s님이 소유한 카드 종류 %d"), *Person->GetName(), CardType);

		// 선언한 열거형에 대한 정보를 얻을 수 있음, UnrealComposition: 모듈 이름.
		const UEnum* CardEnumType = FindObject<UEnum>(nullptr, TEXT("/Script/UnrealComposition.ECardType"));
		if (CardEnumType)
		{
			FString CardMetaData = CardEnumType->GetDisplayNameTextByValue((int64)CardType).ToString();
			UE_LOG(LogTemp, Log, TEXT("%s님이 소유한 카드 종류 %s"), *Person->GetName(), *CardMetaData);
		}
	}
	
	UE_LOG(LogTemp, Log, TEXT("============================"));

}


정리

  • 생성자 코드를 사용해 언리얼 오브젝트를 생성할 수 있음.
  • 언리얼 C++ 컴포지션의 Has-A 관계에 사용되는 용어
    • Subobject
    • Outer
  • 언리얼 C++이 제공하는 확장 열거형을 사용해 메타 정보를 넣고 활용할 수 있음.
profile
명랑코딩!

0개의 댓글