컴포지션(Composition)
컴포지션은 객체를 설계하고 구조화하는 방식 중 하나로, 다른 객체를 조합하여 더 복잡한 기능을 구현하는 것
상속(Inheritance)과 대비되는 개념으로, “객체가 다른 객체를 소유하거나 포함”하는 관계를 통해 재사용성과 유연성을 높이는 방법이다.
객체지향에서 컴포지션의 정의
- 컴포지션은 객체가 다른 객체의 인스턴스를 자신의 멤버 변수로 포함하여 관계를 형성하는 방식이다.
- “Has-a”관계를 표현한다.
- ex) 자동차(Car)는 엔진(Engine)을 “소유”한다. → Car has an Engine.
상속과 컴포지션의 차이
상속(Inheritance) | 컴포지션(Composition) |
“Is-a”관계를 표현 (ex: 사과는 동물이다) | “Has-a”관계를 표현 (ex: 자동차는 엔진을 가진다) |
부모 클래스와 강하게 결합됨 | 약한 결합(Loose coupling) |
코드 재사용을 위해 계층 구조 설계 | 객체를 조합하여 재사용성 구현 |
유연성이 낮음 (변경 시 모든 하위 클래스 영향) | 유연성이 높은 (구성 요소만 교체하면 됨) |
컴포지션의 장점
- 유연성 : 객체를 독립적으로 설계할 수 있어 교체하거나 확장하기 쉬움.
- 재사용성: 개별 구성 요소를 다른 객체에서 재사용 가능.
- 모듈성: 각 객체는 독립적으로 관리되므로 유지보수가 쉬움.
- 상속보다 결합도가 낮음: 부모 클래스의 변경이 영향을 미치지 않음.
예시코드: 상속 vs 컴포지션
상속
#include <bits/stdc++.h>
using namespace std;
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
class Dog : public Animal {
public:
void bark() {
cout << "Barking..." << endl;
}
};
int main() {
Dog* dog = new Dog();
dog->eat();
dog->bark();
delete dog;
}
컴포지션
#include <bits/stdc++.h>
using namespace std;
class Engine {
public:
void start() {
cout << "Engine Started!" << "\n";
}
};
class Car {
private:
Engine engine;
public:
void startCar() {
cout << "Starting the car..." << "\n";
engine.start();
}
};
int main() {
Car myCar;
myCar.startCar();
return 0;
}
결론
- 컴포지션은 객체지향 설계에서 상속의 단점을 보완하는 강력한 설계 기법이다.
- 상속은 계층 구조를 형성하는 데 유용하지만, 유연성이 낮아 코드의 변경이 어렵고 결합도가 높다.
- 컴포지션은 객체를 독립적으로 설계하고 조합하므로 유지보수와 확장이 용이하다, 특히 SOLID원칙(단일 책임 원칙, 개방-폐쇄 원칙)을 따를 때 자주 사용된다.
컴포지션을 활용한 언리얼 오브젝트
내가 소유한 하위 오브젝트 : SubObject
나를 소유한 상위 오브젝트 : Outer
아래 예시코드는 StaticMesh와 Point Light를 컴포지션 해서 SimpleCube를 구성한 컴포지션 예시다.
// SimpleCube.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SimpleCube.generated.h"
UCLASS()
class YOURPROJECT_API ASimpleCube : public AActor
{
GENERATED_BODY()
public:
ASimpleCube();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
private:
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* CubeMesh;
UPROPERTY(VisibleAnywhere)
UPointLightComponent* PointLight;
};
// SimpleCube.cpp
#include "SimpleCube.h"
#include "Components/StaticMeshComponent.h"
#include "Components/PointLightComponent.h"
#include "Engine/StaticMesh.h"
ASimpleCube::ASimpleCube()
{
PrimaryActorTick.bCanEverTick = true;
// 큐브 메쉬 생성
CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));
RootComponent = CubeMesh;
// 기본 큐브 메쉬 로드
static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMeshAsset(TEXT("/Engine/BasicShapes/Cube"));
if (CubeMeshAsset.Succeeded())
{
CubeMesh->SetStaticMesh(CubeMeshAsset.Object);
}
// 포인트 라이트 생성
PointLight = CreateDefaultSubobject<UPointLightComponent>(TEXT("PointLight"));
PointLight->SetupAttachment(CubeMesh);
PointLight->SetIntensity(3000.0f);
PointLight->SetLightColor(FLinearColor::Yellow);
}
void ASimpleCube::BeginPlay()
{
Super::BeginPlay();
}
void ASimpleCube::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}