컴포지션
- 객체 지향 설계에서 상속이 가진 Is-A 관계만 의존해서는 유지보수가 어려움
- 컴포지션은 객체 지향 설계에서 Has-A 관계를 구현하는 설계 방법
- 컴포지션의 활용
- 복합적인 기능을 거대한 클래스를 효과적으로 설계하는데 유용하게 사용할 수 있음.
SOLID 기법
- Single Responsibility Principle (단일 책임 원칙)
- Open-Closed Principle (개방 폐쇄 원칙)
- 기존에 구현된 코드를 변경하지 않으면서 새로운 기능을 추가할 수 있도록 설계
- Liskov Substitution Principle (리스코프 치환 원칙)
- 자식 객체를 부모 객체로 변경해도 작동에 문제 없을 정도로 상속을 단순히 사용
- Interface Segregation Design (인터페이스 분리 원칙)
- 객체가 구현해야 할 기능이 많다면 이들을 여러 개의 단순한 인터페이스들로 분리해 설계
- Dependency Injection Principle (의존성 역전 원칙)
- 구현된 실물보다 구축해야 할 추상적 개념에 의존
컴포지션을 활용해야 할 때는?
새로운 자식이 생기면 부모를 수정해야 될 때.
하지만 부모를 수정하면 기존 자식들에게 문제가 생길 수도 있다.
그러므로 컴포지션으로 분리하는 것이 바람직한데 단순히 컴포지션으로 구현하는 것이 아니라 모던 객체지향 언어가 제공하는 고급 기법들을 활용해야 한다.
언리얼 엔진에서의 컴포지션
- 언리얼 오브젝트를 조합하는 방법
- 필수적 포함 : CDO에 미리 언리얼 오브젝트를 생성해 조합
- CreateDefaultSubobject()사용
- 선택적 포함 : CDO에 빈 포인터만 넣고 런타임에 언리얼 오브젝트를 생성해 조합
- 언리얼 오브젝트는 생성할 때 컴포지션 정보를 자동으로 구축해 줌
실습
- 열거형
- 열거형 E 접두사
- uint8로 해주는 것이 일반적
- UENUM() 매크로를 통해서 언리얼 엔진을 통해 유용한 정보를 가져 올 수 있다.
- 열거형의 각 데이터마다 메타 정보를 넣고 사용할 수 있다.
- UMETA(DisplayeName = "For Student")
- 컴포지션의 경우 헤더를 포함하기 보다는 전방선언
- 보통 오브젝트는 포인터로 관리하기 때문에 전방선언을 통해서 의존성을 최대한 없애기
언리얼 엔진 5 마이그레이션 가이드
Care = CreateDefaultSubobject(TEXT("NAME_Care");
- FindObject를 통해서 열거형 타입 가져오기 가능
- 열거형에 메타데이터를 만들어뒀다면 메타 데이터 가져오기 가능
const UEnum* CardEnumType = FindObject<UEnum>(nullptr, TEXT("/Script/프로젝트 이름.열거형 이름"));
if(CardEnumType)
{
FString CardMetaData = CardEnumType->GetDisplayNameTextByValue(int64)CardType).ToString();
}
정리
- 언리얼 C++은 컴포지션을 구현하는 독특한 패턴이 있다.
- 클래스 기본 객체를 생성하는 생성자 코드를 사용해 복잡한 언리얼 오브젝트를 생성할 수 있음.
- 언리얼 C++ 컴포지션 Has-A 관계에 사용되는 용어
- 내가 소유한 하위 오브젝트 : Subobject
- 나를 소유한 상위 오브젝트 : Outer
언리얼 C++의 컴포지션 기법은 게임의 객체를 설계하고 생성할 때 유용하게 사용된다.