1. 기이한 이름 (Mysterious Name)
나쁜 예시
void DoIt(int x);
float AAA;
int WTF;
좋은 예시
void AttackEnemy(int DamageAmount);
float CurrentHealth;
int EnemyCount;
2. 중복 코드 (Duplicated Code)
나쁜 예시
void PlayerTakeDamage(float Amount)
{
Health -= Amount;
if (Health <= 0)
{
Die();
}
}
void BossTakeDamage(float Amount)
{
Health -= Amount;
if (Health <= 0)
{
SummonMinions();
Die();
}
}
좋은 예시
class ACharacterBase : public AActor
{
protected:
virtual void OnDeath() { }
public:
void TakeDamage(float Amount)
{
Health -= Amount;
if (Health <= 0)
{
OnDeath();
}
}
};
class APlayerCharacter : public ACharacterBase
{
protected:
virtual void OnDeath() override
{
}
};
class ABoss : public ACharacterBase
{
protected:
virtual void OnDeath() override
{
SummonMinions();
}
};
3. 긴 함수 (Long Function)
나쁜 예시
void AMyCharacter::Tick(float DeltaTime)
{
}
좋은 예시
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
HandleMovement(DeltaTime);
HandleJump();
HandleAttack();
UpdateAnimation();
}
void AMyCharacter::HandleMovement(float DeltaTime)
{
}
void AMyCharacter::HandleJump()
{
}
void AMyCharacter::HandleAttack()
{
}
4. 긴 매개변수 목록 (Long Parameter List)
나쁜 예시
void InitWeapon(FString Name, float Damage, float FireRate, int32 AmmoCount, float ReloadTime, USkeletalMesh* Mesh, USoundBase* Sound)
{
}
좋은 예시
struct FWeaponData
{
FString Name;
float Damage;
float FireRate;
int32 AmmoCount;
};
struct FWeaponAssets
{
USkeletalMesh* Mesh;
USoundBase* Sound;
};
void InitWeapon(const FWeaponData& InData, const FWeaponAssets& InAssets)
{
}
5. 전역 데이터 (Global Data)
나쁜 예시
UGameManager* GGameManager;
void IncreaseScore()
{
GGameManager->Score += 10;
}
좋은 예시
UCLASS()
class UScoreSystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
private:
int32 Score;
public:
void AddScore(int32 Amount)
{
Score += Amount;
}
int32 GetScore() const { return Score; }
};
void AEnemy::OnDefeated()
{
if (UGameInstance* GI = GetGameInstance())
{
if (UScoreSystem* ScoreSys = GI->GetSubsystem<UScoreSystem>())
{
ScoreSys->AddScore(50);
}
}
}
6. 가변 데이터 (Mutable Data)
나쁜 예시
class APlayerCharacter : public ACharacter
{
public:
float Health;
int32 Level;
};
void SomeRandomFunc(APlayerCharacter* Player)
{
Player->Health = 99999.f;
Player->Level = 999;
}
좋은 예시
class APlayerCharacter : public ACharacter
{
private:
float Health;
int32 Level;
public:
float GetHealth() const { return Health; }
int32 GetLevel() const { return Level; }
void TakeDamage(float Amount)
{
Health = FMath::Max(0.0f, Health - Amount);
}
void LevelUp()
{
Level++;
Health = 100.f * Level;
}
};
7. 뒤엉킨 변경 (Divergent Change)
나쁜 예시
class AGameManager : public AActor
{
public:
void LoadPlayerData();
void SavePlayerData();
void StartNewGame();
void SpawnEnemies();
private:
FString SaveFilePath;
TArray<AEnemy*> ActiveEnemies;
};
좋은 예시
class UPlayerDataManager : public UGameInstanceSubsystem
{
public:
void LoadPlayerData();
void SavePlayerData();
};
class UGameplayManager : public UGameInstanceSubsystem
{
public:
void StartNewGame();
void SpawnEnemies();
};
8. 샷건 수술 (Shotgun Surgery)
나쁜 예시
class APlayerCharacter : public ACharacter
{
public:
void TakeDamage(float Amount)
{
}
};
class AWeapon : public AActor
{
public:
float CalculateDamage()
{
return 0.0f;
}
};
class AMyGameMode : public AGameModeBase
{
public:
void UpdateDamageLeaderboard()
{
}
};
좋은 예시
class UDamageSystem : public UObject
{
public:
float CalculateDamage(AWeapon* Weapon, ACharacter* Target);
void ApplyDamage(AWeapon* Weapon, ACharacter* Target);
void UpdateDamageLeaderboard(ACharacter* Damager, ACharacter* Target, float Amount);
};
9. 기능 편애 (Feature Envy)
나쁜 예시
class UDamageCalculator : public UObject
{
public:
float CalculateDamageReduction(AMyCharacter* Character, float Damage)
{
float HealthPercent = Character->GetHealth() / Character->GetMaxHealth();
float ArmorFactor = Character->GetArmor() * 0.1f;
return Damage * (1.0f - ArmorFactor * HealthPercent);
}
};
좋은 예시
class AMyCharacter : public ACharacter
{
public:
float CalculateDamageReduction(float Damage) const
{
float HealthPercent = Health / MaxHealth;
float ArmorFactor = Armor * 0.1f;
return Damage * (1.0f - ArmorFactor * HealthPercent);
}
};
class UDamageCalculator : public UObject
{
public:
float CalculateDamageReduction(AMyCharacter* Character, float Damage)
{
return Character->CalculateDamageReduction(Damage);
}
};
10. 데이터 뭉치 (Data Clumps)
나쁜 예시
void FireWeapon(float Damage, float Range, float Accuracy);
void ShowWeaponStats(float Damage, float Range, float Accuracy);
void UpgradeWeapon(float& Damage, float& Range, float& Accuracy);
좋은 예시
USTRUCT(BlueprintType)
struct FWeaponStats
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Damage;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Range;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Accuracy;
};
void FireWeapon(const FWeaponStats& Stats);
void ShowWeaponStats(const FWeaponStats& Stats);
void UpgradeWeapon(FWeaponStats& Stats);
11. 기본형 집착 (Primitive Obsession)
나쁜 예시
float Health;
float MaxHealth;
FString PhoneNumber;
좋은 예시
class FHealth
{
public:
FHealth(float InCurrent, float InMax)
: Current(InCurrent), Max(InMax) {}
void ApplyDamage(float Amount)
{
Current = FMath::Max(0.f, Current - Amount);
}
private:
float Current;
float Max;
};
class FPhoneNumber
{
public:
FPhoneNumber(const FString& InNumber)
{
}
private:
FString Number;
};
12. 반복되는 스위치문 (Repeated Switches)
나쁜 예시
switch (WeaponType)
{
case EWeaponType::Sword:
return DoSwordAttack();
case EWeaponType::Bow:
return DoBowAttack();
}
좋은 예시
class AWeapon : public AActor
{
public:
virtual void Attack();
};
class ASword : public AWeapon
{
public:
virtual void Attack() override { }
};
class ABow : public AWeapon
{
public:
virtual void Attack() override { }
};
13. 반복문 (Loops)
나쁜 예시
void ProcessHeavyItems()
{
TArray<UItem*> Items = GetAllItems();
TArray<UItem*> HeavyItems;
for (int32 i = 0; i < Items.Num(); i++)
{
if (Items[i]->Weight > 10.f)
{
HeavyItems.Add(Items[i]);
}
}
float TotalWeight = 0.f;
for (int32 j = 0; j < HeavyItems.Num(); j++)
{
TotalWeight += HeavyItems[j]->Weight;
}
if (TotalWeight > 50.f)
{
ApplySlowEffect();
}
}
좋은 예시
void ProcessHeavyItems()
{
TArray<UItem*> Items = GetAllItems();
auto HeavyItems = Items.FilterByPredicate([](UItem* Item)
{
return Item->Weight > 10.f;
});
float TotalWeight = 0.f;
for (UItem* Item : HeavyItems)
{
TotalWeight += Item->Weight;
}
if (TotalWeight > 50.f)
{
ApplySlowEffect();
}
}
14. 게으른 요소 (Lazy Element)
나쁜 예시
class AProjectile : public AActor
{
public:
void Launch(const FVector& Dir, float Speed)
{
LaunchProjectile(Dir, Speed);
}
private:
void LaunchProjectile(const FVector& Dir, float Speed)
{
ProjectileMovement->Velocity = Dir * Speed;
}
};
좋은 예시
class AProjectile : public AActor
{
public:
void Launch(const FVector& Dir, float Speed)
{
ProjectileMovement->Velocity = Dir * Speed;
}
private:
UProjectileMovementComponent* ProjectileMovement;
};
15. 추측성 일반화 (Speculative Generality)
나쁜 예시
class AWeapon : public AActor
{
public:
virtual void APlayer::PlayWeaponSound()
{
USoundBase* AttackSound = GetEquippedWeaponSound();
if (AttackSound)
{
UGameplayStatics::PlaySound2D(this, AttackSound);
}
}
USoundBase* APlayer::GetEquippedWeaponSound()
{
return Inventory ? Inventory->GetAttackSound() : nullptr;
}
USoundBase* UInventoryComponent::GetAttackSound()
{
if (!EquippedWeapon) return nullptr;
return EquippedWeapon->GetAttackSound();
}
USoundBase* AWeapon::GetAttackSound()
{
return SoundData ? SoundData->AttackSound : nullptr;
}ttack();
virtual void SpecialAttack();
virtual void UltimateAttack();
virtual void ElementalAttack();
void SetDamage(float BaseDamage, float Crit, float Splash, float Chain, float Summon);
};
좋은 예시
class AWeapon : public AActor
{
public:
void Attack();
void SetDamage(float InDamage);
private:
float Damage;
};
class AMagicWeapon : public AWeapon
{
void ElementalAttack();
};
16. 임시 필드 (Temporary Field)
나쁜 예시
class AEnemy : public ACharacter
{
public:
float Health;
float ProjectileSpeed;
UParticleSystem* ProjectileEffect;
float TeleportCooldown;
float LastTeleportTime;
};
좋은 예시
class URangedAttackComponent : public UActorComponent
{
float ProjectileSpeed;
void ExecuteAttack();
};
class UTeleportComponent : public UActorComponent
{
float TeleportCooldown;
void ExecuteTeleport();
};
class AEnemy : public ACharacter
{
float Health;
URangedAttackComponent* RangedComp;
UTeleportComponent* TeleportComp;
};
17. 메시지 체인 (Message Chains)
나쁜 예시
void APlayer::PlayWeaponSound()
{
if (Inventory
&& Inventory->EquippedWeapon
&& Inventory->EquippedWeapon->SoundData
&& Inventory->EquippedWeapon->SoundData->AttackSound)
{
UGameplayStatics::PlaySound2D(this, Inventory->EquippedWeapon->SoundData->AttackSound);
}
}
좋은 예시
void APlayer::PlayWeaponSound()
{
USoundBase* AttackSound = GetEquippedWeaponSound();
if (AttackSound)
{
UGameplayStatics::PlaySound2D(this, AttackSound);
}
}
USoundBase* APlayer::GetEquippedWeaponSound()
{
return Inventory ? Inventory->GetAttackSound() : nullptr;
}
USoundBase* UInventoryComponent::GetAttackSound()
{
if (!EquippedWeapon) return nullptr;
return EquippedWeapon->GetAttackSound();
}
USoundBase* AWeapon::GetAttackSound()
{
return SoundData ? SoundData->AttackSound : nullptr;
}
18. 중재자 (Middle Man)
나쁜 예시
class AMyPlayerController : public APlayerController
{
public:
void MoveForward(float Value) { Character->MoveForward(Value); }
void MoveRight(float Value) { Character->MoveRight(Value); }
void Jump() { Character->Jump(); }
void StartFire() { Character->StartFire(); }
void StopFire() { Character->StopFire(); }
private:
AMyCharacter* Character;
};
좋은 예시
void AMyPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
AMyCharacter* MyChar = Cast<AMyCharacter>(GetCharacter());
if (MyChar && InputComponent)
{
MyChar->SetupPlayerInput(InputComponent);
}
}
void AMyCharacter::SetupPlayerInput(UInputComponent* PlayerInputComponent)
{
PlayerInputComponent->BindAxis("MoveForward", this, &AMyCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMyCharacter::MoveRight);
}
19. 내부자 거래 (Insider Trading)
나쁜 예시
void AEnemy::Attack(APlayerCharacter* Player)
{
if (!Player->bIsInvulnerable)
{
float Damage = AttackDamage - Player->EquippedArmor->DamageReduction;
Player->CurrentHealth -= Damage;
Player->PlayerHUD->UpdateHealthBar(Player->CurrentHealth, Player->MaxHealth);
}
}
좋은 예시
void AEnemy::Attack(APlayerCharacter* Player)
{
if (Player && Player->CanBeAttacked())
{
Player->ReceiveDamage(AttackDamage);
}
}
bool APlayerCharacter::CanBeAttacked() const
{
return !bIsInvulnerable;
}
void APlayerCharacter::ReceiveDamage(float Damage)
{
float ActualDamage = EquippedArmor ?
EquippedArmor->ApplyReduction(Damage) : Damage;
CurrentHealth = FMath::Max(0.f, CurrentHealth - ActualDamage);
PlayerHUD->UpdateHealth(CurrentHealth, MaxHealth);
}
20. 거대한 클래스 (Large Class)
나쁜 예시
class AGameCharacter : public ACharacter
{
public:
void MoveForward(float Value);
void MoveRight(float Value);
void Attack();
void Reload();
void AddItem(UItem* Item);
void RemoveItem(UItem* Item);
void AcceptQuest(UQuest* Quest);
void CompleteQuest(UQuest* Quest);
void StartDialogue();
void EndDialogue();
};
좋은 예시
class AGameCharacter : public ACharacter
{
public:
AGameCharacter();
private:
UPROPERTY()
UMovementComponent* MovementComp;
UPROPERTY()
UCombatComponent* CombatComp;
UPROPERTY()
UInventoryComponent* InventoryComp;
UPROPERTY()
UQuestComponent* QuestComp;
};
21. 서로 다른 인터페이스의 대안 클래스들 (Alternative Classes with Different Interfaces)
나쁜 예시
class ARangedWeapon
{
public:
void FireProjectile();
void Reload();
};
class AMeleeWeapon
{
public:
void PerformAttack();
void SharpenBlade();
};
void APlayerCharacter::Attack()
{
if (CurrentRangedWeapon)
CurrentRangedWeapon->FireProjectile();
else if (CurrentMeleeWeapon)
CurrentMeleeWeapon->PerformAttack();
}
좋은 예시
class AWeapon : public AActor
{
public:
virtual void Attack() = 0;
virtual void Reload() {}
};
class ARangedWeapon : public AWeapon
{
public:
virtual void Attack() override { }
virtual void Reload() override { }
};
class AMeleeWeapon : public AWeapon
{
public:
virtual void Attack() override { }
};
void APlayerCharacter::Attack()
{
if (CurrentWeapon)
{
CurrentWeapon->Attack();
}
}
22. 데이터 클래스 (Data Class)
나쁜 예시
class FPlayerStats
{
public:
float GetHealth() const { return Health; }
void SetHealth(float H) { Health = H; }
private:
float Health;
float MaxHealth;
};
void APlayerCharacter::TakeDamage(float Damage)
{
float NewHealth = PlayerStats.GetHealth() - Damage;
PlayerStats.SetHealth(FMath::Max(0.f, NewHealth));
}
좋은 예시
class FPlayerStats
{
public:
void ApplyDamage(float Damage)
{
float ActualDamage = Damage * (1.0f - Defense / 100.f);
Health = FMath::Max(0.f, Health - ActualDamage);
}
bool IsDead() const { return Health <= 0.f; }
private:
float Health;
float Defense;
};
void APlayerCharacter::TakeDamage(float Damage)
{
PlayerStats.ApplyDamage(Damage);
if (PlayerStats.IsDead())
{
Die();
}
}
23. 상속 포기 (Refused Bequest)
나쁜 예시
class AWeapon
{
public:
virtual void Attack();
virtual void Reload();
};
class AMeleeWeapon : public AWeapon
{
public:
virtual void Reload() override
{
}
};
좋은 예시
class ABaseWeapon : public AActor
{
public:
virtual void Attack() = 0;
};
class ARangedWeapon : public ABaseWeapon
{
public:
virtual void Attack() override { }
void Reload() { }
};
class AMeleeWeapon : public ABaseWeapon
{
public:
virtual void Attack() override { }
};
나쁜 예시
void AEnemy::UpdateBehavior()
{
...
}
좋은 예시
void AEnemy::UpdateBehavior()
{
if (CanSeePlayer())
{
EngagePlayer();
}
else
{
PatrolArea();
}
}
bool AEnemy::CanSeePlayer()
{
return IsWithinSightRange() && IsInFieldOfView() && HasLineOfSight();
}