Interface 개념
자식 클래스가 반드시 구현해야 할 “행동” 혹은 "기능"을 지정하는데 활용되는 클래스이다
예를 들어,
비둘기와 까마귀 클래스를 만든다면
새는 “날아가기”라는 행동을 하기에
"날아가기"와 관련된 인터페이스를 만들어서 Fly() 함수를 만들고
비둘기와 까마귀 클래스에 해당 인터페이스를 상속받아서 Fly() 함수를 정의하게 하는 것이다
Interface 목적
다형성의 구현과 의존성이 분리된(Decouple) 설계에 유용하게 활용된다
예를 들어,
새의 주인 클래스가 있고 주인이 새를 날린다고 가정하자
주인 클래스는 무슨 새인지, 새가 어떻게 날아가는지 전혀 알지 못해도
그저 나는 동작을 실행시키기만 하면 된다
따라서,
인터페이스의 Fly() 함수를 통해서 새와 연결시키고
이를 통해, 그 어떤 새의 종류 클래스 내에서 문제가 발생해서 수정하더라도
해당 인터페이스가 바뀌지 않는 한, 새 주인 클래스는 재컴파일 되지 않는다
Interface 주의점
인터페이스를 생성하면 두 개의 클래스가 하나의 파일에 작성이 된다
U로 시작하는 타입 클래스와 I로 시작하는 인터페이스 클래스로 구분된다
런타임에서 클래스 타입 정보를 제공할 때는 타입 클래스,
개체를 설계할 때는 인터페이스 클래스를 사용한다
실제로는 타입 클래스를 거의 사용하지 않는다
추가적으로, 다른 언어와는 다르게 Unreal C++ Interface에는 선언과 동시에 정의가 가능하며
반드시 순수가상함수일 필요는 없지만 대부분 순수가상함수를 작성한다
Interface 예시
1) Interface 생성
// SFlyable.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "SFlyable.generated.h"
UINTERFACE(MinimalAPI)
class USFlyable : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class STUDYPROJECT_API ISFlyable
{
GENERATED_BODY()
public:
virtual void Fly() = 0;
// ISFlyable 인터페이스에서 구현 해도되고 안해도 되며, 일반적으로는 구현하지 않는다
};
// SFlyable.cpp
#include "Example/SFlyable.h"
2) Interface를 상속받는 Pigeon Class
// SPigeon.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "SFlyable.h"
#include "SPigeon.generated.h"
/**
*
*/
UCLASS()
class STUDYPROJECT_API USPigeon
: public UObject
, public ISFlyable
{
GENERATED_BODY()
public:
USPigeon();
virtual void Fly() override;
private:
UPROPERTY()
FString Name;
};
// SPigeon.cpp
#include "Example/SPigeon.h"
USPigeon::USPigeon()
{
Name = TEXT("Pigeon");
}
void USPigeon::Fly()
{
UE_LOG(LogTemp, Log, TEXT("%s is now flying."), *Name);
}
3) Interface를 상속받는 Eagle Class
// SEagle.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "SFlyable.h"
#include "SEagle.generated.h"
/**
*
*/
UCLASS()
class STUDYPROJECT_API USEagle
: public UObject
, public ISFlyable
{
GENERATED_BODY()
public:
USEagle();
virtual void Fly() override;
private:
UPROPERTY()
FString Name;
};
// SEagle.cpp
#include "Example/SEagle.h"
USEagle::USEagle()
{
Name = TEXT("Eagle");
}
void USEagle::Fly()
{
UE_LOG(LogTemp, Log, TEXT("%s is now flying."), *Name);
}
4) Interface 활용
// SGameInstance.cpp
#include "Game/SGameInstance.h"
#include "Example/SFlyable.h"
#include "Example/SPigeon.h"
#include "Example/SEagle.h"
USGameInstance::USGameInstance()
{
}
void USGameInstance::Init()
{
Super::Init();
USPigeon* Pigeon1 = NewObject<USPigeon>();
USEagle* Eagle1 = NewObject<USEagle>();
TArray<ISFlyable*> Birds;
Birds.Reserve(10);
if (Pigeon1->GetClass()->ImplementsInterface(USFlyable::StaticClass()) == true)
{
ISFlyable* Bird1 = Cast<ISFlyable>(Pigeon1);
Birds.Emplace(Bird1);
}
if (Eagle1->GetClass()->ImplementsInterface(USFlyable::StaticClass()) == true)
{
ISFlyable* Bird2 = Cast<ISFlyable>(Eagle1);
Birds.Emplace(Bird2);
}
for (ISFlyable* Bird : Birds)
{
Bird->Fly();
// 다형성 예시로 같은 클래스(ISFlyable)지만 다른 행동(Pigeon is ~, Eagle is ~)을 한다
// 동시에 의존성 디커플링이기도 하다
// SGameInstance 클래스는 어떻게 나는지, 어떤 클래스인지 알필요 없고
// Fly() 함수를 호출 할 수 있기만 하면 된다
}
}
void USGameInstance::Shutdown()
{
Super::Shutdown();
}
'게임 엔진 > [코드조선] 언리얼' 카테고리의 다른 글
언리얼 오브젝트 기능 - C++ 로우 포인터 문제 해결 (0) | 2024.05.09 |
---|---|
언리얼 오브젝트 기능 - Garbage Collection (0) | 2024.05.09 |
언리얼 오브젝트 기능 - 리플렉션 (0) | 2024.05.09 |
언리얼 오브젝트 기능 - CDO (0) | 2024.05.09 |
언리얼 기초 - 언리얼 디버깅 (0) | 2024.05.08 |