Service Locator
- Locator 클래스가 서비스 객체의 인스턴스를 관리하고 제공
- 클라이언트는 서비스의 구현을 직접 참조하지 않고 Locator를 통해 서비스에 접근
- 모듈화 수준을 높이며 클라이언트와 인터페이스 사이의 의존성을 제거한다.
- Factory패턴과 유사하지만, Locator는 이미 생성된 객체를 제공한다.
- 자주 참조하는 객체를 가져올 때 사용할 수 있다.
- 멀티플레이 Server-Client 구조의 Network(Locator)를 사용한 서비스 접근 방식
- ActorComponent를 사용하여 응집도를 높이며 Actor들을 관리하는 방식
장점 및 단점
- 런타임 최적화 : 더 최적화된 라이브러리 또는 컴포넌트를 동적으로 감지하여 최적화 가능
- 구현하기 쉽다.
- Mock 객체 주입이 불가능하여 단위테스트 하기 어렵다.
- ServiceLocator를 직접 참조하는 형태로, 결합도 또한 높다.
예시코드
WeaponComponent.cpp
// 장지 장착 로직
void UWeaponComponent::EquipWeapon(EWeaponType Slot, TSubclassOf<class ABaseWeapon> WeaponClass)
{
ACharacter* TargetOwner = Cast<ACharacter>(GetOwner());
if (!TargetOwner || !WeaponClass)
{
UE_LOG(LogWeapon, Error, TEXT("WeaponComponent_EquipWeapon Func Owner || WeaponClass nullptr"));
return;
}
if (EquipWeapons.Contains(Slot) && EquipWeapons[Slot])
{
EquipWeapons[Slot]->Destroy();
EquipWeapons[Slot] = nullptr;
}
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = TargetOwner;
SpawnParams.Instigator = TargetOwner->GetInstigator();
ABaseWeapon* NewWeapon = GetWorld()->SpawnActor<ABaseWeapon>(WeaponClass, SpawnParams);
if (NewWeapon)
{
NewWeapon->AttachToComponent(TargetOwner->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true),
WeaponAttachSocketName);
NewWeapon->SetActorRelativeLocation(NewWeapon->GetSocketOffset());
NewWeapon->SetActorRelativeRotation(NewWeapon->GetSocketRotation());
NewWeapon->SetActorEnableCollision(false);
EquipWeapons.Add(Slot, NewWeapon);
SwitchWeapon(Slot);
}
}
// 장비 교체 로직
void UWeaponComponent::SwitchWeapon(EWeaponType Slot)
{
ACharacter* TargetOwner = Cast<ACharacter>(GetOwner());
if (!TargetOwner || !EquipWeapons.Contains(Slot))
{
UE_LOG(LogWeapon, Error, TEXT("WeaponComponent_SwitchWeapon Func Owner || Target Weapon Slot is nullptr"));
return;
}
if (CurrentWeapon)
{
UnEquipWeapon();
}
CurrentWeapon = EquipWeapons[Slot];
if (CurrentWeapon)
{
CurrentWeapon->SetActorHiddenInGame(false);
}
}
Player.h
// Weapons
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon")
UWeaponComponent* WeaponComponent;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon")
TSubclassOf<class ABaseWeapon> WeaponClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon")
TSubclassOf<class ABaseWeapon> SubWeaponClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon")
TSubclassOf<class ABaseWeapon> MeleeWeaponClass;
Player.cpp
void APlayer::SpawnActorComponent()
{
WeaponComponent = CreateDefaultSubobject<UWeaponComponent>(TEXT("Weapon"));
if (!WeaponComponent)
{
UE_LOG(LogPlayer, Error, TEXT("WeaponComponent CDO Fail"));
}
}
// TODO :Event 형식으로 UI와 연동, 해당 WeaponClass를 WeaponComponent에 추가 요청.
void APlayer::AttachWeapon()
{
if (WeaponComponent)
{
WeaponComponent->EquipWeapon(EWeaponType::Rifle, WeaponClass);
WeaponComponent->EquipWeapon(EWeaponType::Pistol, SubWeaponClass);
WeaponComponent->EquipWeapon(EWeaponType::Melee, MeleeWeaponClass);
}
}
// 장비 변경
void APlayer::SwitchCurrentWeapon(int32 WeaponType)
{
UE_LOG(LogPlayer, Warning, TEXT("WeaponType Call : %d"), int32(WeaponType));
if (WeaponComponent)
{
WeaponComponent->SwitchWeapon((EWeaponType)WeaponType);
}
}
예시코드 결과
- WeaponComponent(Locator)를 통해 Weapon 관련 클래스들을 제공하여 응집도를 높일 수 있다.
- Player - Weapon 클래스간 낮은 결합도를 유지할 수 있다.
- Weapon(서비스) 관리가 용이하며, 해당 클래스를 단순하게 사용할 수 있다.
- WeaponComponent가 Player 클래스에 직접 주입되는 형태로, 완전한 Service Locator가 아닌 상태.
'디자인패턴' 카테고리의 다른 글
[디자인 패턴] 옵저버 패턴 (Observer Pattern) (0) | 2025.02.28 |
---|---|
[디자인패턴] - MVC, MVVM 패턴 (1) | 2025.02.18 |
[디자인패턴] - 프록시 패턴 (Proxy Pattern) (0) | 2025.02.07 |
Factory Method Pattern (0) | 2024.12.24 |
Command Pattern (2) | 2024.12.23 |