컴포지션
- 객체 지향 설계에서 상속이 가진 Is-A 관계만 의존해서는 설계와 유지보수가 어려움
- 컴포지션은 객체 지향 설계에서 Has-A 관계를 구현하는 설계 방법
SOLID 기법
- Single Responsibility Principle (단일 책임 원칙)
- 하나의 객체는 하나의 의무만 가지도록 설계
- Open-Closed Principle (개방 폐쇄 원칙)
- 기존에 구현된 코드를 변경하지 않으면서 새로운 기능을 추가할 수 있도록 설계
- Liskov Substitution Principle (리스코프 치환 원칙)
- 자식 객체를 부모 객체로 변경해도 작동에 문제 없을 정도로 상속을 단순히 사용
- Interface Segregation Design (인터페이스 분리 원칙)
- 객체가 구현해야 할 기능이 많다면 이들을 여러 개의 단순한 인터페이스들로 분리해 설계
- Dependency Injection Principle (의존성 역전 원칙)
- 구현된 실물보다 구축해야 할 추상적 개념에 의존
실습 예시
- 출입증 기능
- 출입증을 부모에서 구현해 상속 or 컴포지션 분리
- 새로운 자식이 생기면 부모를 수정해야될 수도 있음
- 부모를 수정하면 기존 자식들에게 문제가 생길 수도 있음
- 그러므로 컴포지션으로 분리하는 것이 바람직
- 단순히 컴포지션으로 구현하는 것이 아니라 모던 객체지향 언어가 제공하는 고급 기법들을 활용해야 함
언리얼 엔진에서의 컴포지션
- 언리얼 오브젝트를 조합하는 방법
- 필수적 포함: CDO에 미리 언리얼 오브젝트를 생성해 조합
- CreateDefaultSubobject() 사용
- 선택적 포함: CDO에 빈 포인터만 넣고 런타임에 언리얼 오브젝트를 생성해 조합
- NewObject() 사용
- 언리얼 오브젝트는 생성할 때 컴포지션 정보를 자동으로 구축해 줌
- 내가 소유한 언리얼 오브젝트: Subobject
- 나를 소유한 언리얼 오브젝트: Outer
실습
- 오브젝트 구분 -> enum class
- 열거형 E 접두사
- uint8로 해주는 것이 일반적
- UENUM() 매크로를 통해서 언리얼 엔진을 통해 유용한 정보를 가져올 수 있게 됨
- 열거형의 각 데이터마다 메타 정보를 넣고 사용할 수 있음
- UMETA(DisplayName = "For Student")
- 컴포지션의 경우 헤더를 포함하기 보다는 전방선언
- 보통 오브젝트는 포인터로 관리하기 때문에 전방선언을 통해서 의존성을 최대한 없앨 수 있음
언리얼 엔진 5 마이그레이션 가이드 5.0
https://docs.unrealengine.com/5.0/ko/unreal-engine-5-migration-guide/
- 근데 언리얼 5부터 새로운 가이드가 생김
- UPROPERTY 변수에 원시 포인터가 있었던 선언들을 TObejectPtr을 통해서 변경해서 사용
- 선택 사항이지만 권장
- 선언에서 사용하고 구현부에서는 그냥 포인터 써도 됨
- 초기화 단계에서 CreateDefaultSubobject를 통해서 해야 함
- 첫 번째 인자는 FNAME 식별자
- 일반 스트링이 아니라 FNAME인 것을 구분하고 싶으면 접두사에 NAME_를 붙여 줌
- 부모에서 CreateDefaultSubobject를 했으면 자식에선 setter만 호출해주면 됨
- FindObject를 통해서 열거형 타입을 가져옴
FindObject<UEnum>(nullptr, TEXT("/Script/프로젝트 이름.열거형 이름"));
- Script 주소 안에 모듈 이름 == 프로젝트 이름
- 모듈 이름에서 타입 이름을 통해서 정보를 얻어 옴
- GetDisplayNameTextByValue 함수를 통해서 메타 데이터를 가져옴 + ToString()