개발/Unity 3D

[Unity] Unity 프로그래밍에서의 응집도, 결합도, 의존성

귀뚜래미 2025. 6. 11. 22:52
728x90

1. 응집도 (Cohesion)

정의

  • 하나의 모듈(클래스, 스크립트)이 단일 책임 혹은 밀접하게 관련된 기능들만을 모아 수행하는 정도

특징

  • 높을수록 유지보수, 확장이 쉬워짐
  • 한 모듈의 변경이 내부 기능에만 영향을 주므로 부작용이 적음

예시

/// <summary>
/// 플레이어 체력 관리만 담당하는 클래스 (응집도 높음)
/// 체력 계산에만 집중하고 화면표시나 사망 이펙트 등은 별도 모듈이 담당하도록 분리
/// </summary>
public class PlayerHealth : MonoBehaviour
{
    [SerializeField] private int maxHealth = 100;
    private int currentHealth;

    void Awake()
    {
        currentHealth = maxHealth;
    }

    /// <summary>
    /// 데미지를 받아 체력을 감소
    /// </summary>
    public void TakeDamage(int damage)
    {
        currentHealth -= damage;
        currentHealth = Mathf.Max(currentHealth, 0);
        // 이 클래스는 UI 업데이트나 사망 처리 로직을 직접 수행하지 않음
    }

    /// <summary>
    /// 현재 체력을 반환
    /// </summary>
    public int GetCurrentHealth() => currentHealth;
}

 

 

 

2. 결합도 (Coupling)

정의

  • 한 모듈이 다른 모듈에 얼마나 강하게 의존하는지 나타내는 척도

특징

  • 낮을수록 모듈 변경 시 연쇄적인 수정 범위가 줄고 재사용성과 테스트 용이성 높아짐
  • 높을수록 한 클래스 수정 시 다른 클래스 다수 수정 발생, 단위 테스트 어려움

예시

/// <summary>
/// 높은 결함도 예시
/// PlayerController가 EnemyController 내부 구현에 직접 의존
/// </summary>
public class PlayerController : MonoBehaviour
{
    public EnemyController enemy;  // EnemyController에 직접 참조

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            enemy.TakeDamage(10);    // 적 체력 메서드 직접 호출
        }
    }
}








/// <summary>
/// 낮은 결함도 예시
/// 발행자와 구독자가 인터페이스(이벤트)로만 연결되어 서로 내부 구현 몰라도 동작
/// 데미지 이벤트를 발행(Publisher)하는 클래스
/// </summary>
public class DamagePublisher : MonoBehaviour
{
    public static event Action<int> OnDamage;
    
    public void DealDamage(int amount)
    {
        OnDamage?.Invoke(amount);
    }
}

/// <summary>
/// 데미지 이벤트를 구독(Subscriber)하여 체력 처리하는 클래스
/// </summary>
public class EnemyHealth : MonoBehaviour
{
    private int health = 100;

    void OnEnable()
    {
        DamagePublisher.OnDamage += HandleDamage;
    }

    void OnDisable()
    {
        DamagePublisher.OnDamage -= HandleDamage;
    }

    /// <summary>
    /// 이벤트를 통해 전달된 데미지를 처리
    /// </summary>
    private void HandleDamage(int dmg)
    {
        health -= dmg;
        Debug.Log($"남은 체력: {health}");
    }
}

 

 

 

 

 

3. 의존성 (Dependency)

정의

  • 한 모듈이 작동하기 위해 다른 모듈이나 서비스, 데이터를 어떤방식으로 사용하는지를 의미

특징

  • 명시적 의존성 : 생성자 주입(DI), 인터페이스 활용 -> 테스트 및 교체 용이
  • 암시적 의존성 : 싱글톤, Static, FindObjectOfType 등 -> 숨겨진 연결, 가독성, 테스트성 저하

예시

/// <summary>
/// 암시적 의존성
/// 언제 어디서 AudioManager가 생성되는지 불분명, 데스트 어려움
/// </summary>
public class GameManager : MonoBehaviour
{
    private AudioManager audioMgr;

    void Start()
    {
        audioMgr = FindObjectOfType<AudioManager>();  // Find 탐색
        audioMgr.PlayBgm();
    }
}







/// <summary>
/// 명시적 의존성
/// 어떤 IAudioService 구현이 들어올지 모듈 간 결합도를 낮추고, 단위 테스트 시 모의(Mocking) 객체 주입 가능
/// </summary>
public interface IAudioService
{
    void PlayBgm();
}

public class AudioManager : MonoBehaviour, IAudioService
{
    public void PlayBgm() { /* ... */ }
}

/// <summary>
/// 생성자 주입 방식으로 IAudioService를 받는 예시
/// </summary>
public class GameManager : MonoBehaviour
{
    private IAudioService _audioService;

    //  의존성 주입(DI) 적용
    public void Construct(IAudioService audioService)
    {
        _audioService = audioService;
    }

    void Start()
    {
        _audioService.PlayBgm();
    }
}

 

 

 

정리

  • 높은 응집도: 한 클래스가 하나의 책임에 집중 → 유지보수·이해 용이
  • 낮은 결합도: 모듈 간 인터페이스·이벤트로만 연결 → 수정 충돌 최소화
  • 명시적 의존성 관리: DI·인터페이스 활용 → 교체·테스트 편의성 향상
728x90