"깊이"가 다른 게임개발자 허민영

유저에서 게임까지, 철학에서 코딩까지, 본질을 보는 게임개발

소프트웨어 공학/코딩

클래스 관계의 종류와 객체지향 설계에서의 활용

허민영 2025. 3. 17. 23:28

.

1. 클래스 관계의 주요 유형

클래스 간의 관계는 크게 **연관(Association), 집합(Aggregation), 합성(Composition), 의존(Dependency), 상속(Inheritance), 구현(Implementation)**으로 나뉜다.

이제 각 관계를 상세히 살펴보자.


(1) 연관 관계 (Association)

연관 관계는 한 클래스가 다른 클래스를 참조하는 가장 일반적인 관계이다.

  • 양방향 또는 단방향 연관이 가능하다.
  • 클래스 간 독립성이 유지되며, 필요할 때 참조할 수 있다.

예제: 플레이어와 무기 관계

public class Player
{
    public Weapon weapon; // Player가 Weapon을 참조 (단방향 연관)
}

public class Weapon
{
    public string Name;
}

여기서 Player는 Weapon을 참조하지만, Weapon은 Player를 알지 못한다.
만약 양방향 연관을 원한다면, Weapon 클래스에서 Player를 참조하면 된다.

public class Weapon
{
    public Player owner;
}

활용 사례 (Unity)

  • Player ↔ Weapon (플레이어가 무기를 가짐)
  • Enemy ↔ Target (적이 특정 대상을 공격함)

(2) 집합 관계 (Aggregation)

집합 관계는 연관 관계의 특수한 형태로, 한 클래스가 여러 개의 다른 객체를 포함하지만, 객체의 생명주기를 관리하지 않는 경우를 의미한다.

예제: 팀과 플레이어 관계

public class Team
{
    public List<Player> players = new List<Player>();
}

 

  • Team은 여러 개의 Player를 포함하지만, Team이 삭제되어도 Player 객체는 그대로 남는다.
  • 즉, 개별 Player는 독립적으로 존재할 수 있다.

활용 사례 (Unity)

  • Team ↔ Player (팀이 여러 플레이어를 포함)
  • Inventory ↔ Item (인벤토리에 여러 아이템 포함)

(3) 합성 관계 (Composition)

합성 관계는 집합 관계와 비슷하지만, 한 클래스가 포함된 객체의 생명주기를 책임지는 경우를 의미한다.
즉, 부모 객체가 삭제되면 자식 객체도 삭제된다.

예제: 자동차와 엔진 관계

public class Engine
{
    public void Start() => Console.WriteLine("엔진 가동!");
}

public class Car
{
    private Engine engine = new Engine(); // Car가 Engine을 소유

    public void StartCar()
    {
        engine.Start();
    }
}

여기서 Car가 삭제되면 Engine도 삭제된다.

 활용 사례 (Unity)

  • Character ↔ HealthComponent (캐릭터가 체력 컴포넌트를 소유)
  • Enemy ↔ AIController (적이 AI 컨트롤러를 소유)

(4) 의존 관계 (Dependency)

의존 관계는 한 클래스가 다른 클래스를 잠시 참조하는 관계를 의미한다.
즉, 메서드의 매개변수나 반환값으로 전달될 때 관계가 형성된다.

예제: 아이템 사용

public class Item
{
    public string Name;
}

public class Player
{
    public void UseItem(Item item)
    {
        Console.WriteLine($"{item.Name}을(를) 사용했습니다.");
    }
}
  • Player는 Item을 참조하지만, 일시적인 관계일 뿐, Item의 생명주기를 관리하지 않는다.

 활용 사례 (Unity)

  • Player ↔ Potion (플레이어가 포션을 사용)
  • Weapon ↔ Enemy (무기가 적에게 데미지를 줌)

(5) 상속 관계 (Inheritance)

상속은 부모 클래스의 기능을 자식 클래스가 물려받는 관계이다.
C#에서는 :을 사용하여 상속을 표현한다.

예제: 캐릭터와 플레이어, 적 관계

public class Character
{
    public int Health;
    public void Move() => Console.WriteLine("이동 중...");
}

public class Player : Character
{
    public void Attack() => Console.WriteLine("플레이어 공격!");
}

public class Enemy : Character
{
    public void Roar() => Console.WriteLine("적이 포효함!");
}
  • Player와 Enemy는 Character를 상속받아 공통 기능(Health, Move())을 공유한다.
  • 상속을 사용할 경우 코드 중복을 줄일 수 있다.

활용 사례 (Unity)

  • Character ↔ Player, Enemy (캐릭터를 상속)
  • UIElement ↔ Button, Panel (UI 요소를 상속)

주의:

  • 과도한 상속은 유지보수를 어렵게 만들 수 있음
  • 상속 대신 인터페이스를 고려하는 것이 유리한 경우도 많음

(6) 인터페이스 구현 (Implementation)

인터페이스는 **구현해야 할 메서드나 속성을 강제하는 계약(Contract)**이다.
C#에서는 interface 키워드를 사용하며, 다중 구현이 가능하다.

예제: 상호작용 가능한 객체 만들기

public interface IInteractable
{
    void Interact();
}

public class Door : IInteractable
{
    public void Interact() => Console.WriteLine("문을 엽니다.");
}

public class NPC : IInteractable
{
    public void Interact() => Console.WriteLine("NPC와 대화합니다.");
}
  • IInteractable을 구현한 클래스는 반드시 Interact() 메서드를 제공해야 한다.

활용 사례 (Unity)

  • IInteractable ↔ Door, NPC, Item (상호작용 가능한 객체)
  • IDamageable ↔ Player, Enemy (데미지를 받을 수 있는 객체)

7. 클래스 관계 정리

관계 설명 예제
연관 (Association) 한 클래스가 다른 클래스를 참조 Player ↔ Weapon
집합 (Aggregation) 한 클래스가 여러 개의 객체를 포함하지만 생명주기를 관리하지 않음 Inventory ↔ Item
합성 (Composition) 한 클래스가 포함된 객체의 생명주기를 책임짐 Car ↔ Engine
의존 (Dependency) 일시적으로 참조하는 관계 Player ↔ Potion
상속 (Inheritance) 부모 클래스를 자식 클래스가 확장 Character ↔ Player, Enemy
인터페이스 구현 (Implementation) 클래스가 특정 동작을 보장해야 할 때 사용 IInteractable ↔ NPC, Door

8. 결론

클래스 간 관계를 잘 설계하면 코드의 유지보수성과 확장성이 극대화된다.

  • **강한 결합(Composition) vs. 약한 결합(Aggregation, Dependency)**을 고려할 것
  • 상속보다는 인터페이스와 조합(Composition)이 더 유리할 수도 있음

이제 실제 프로젝트에서 적절한 관계를 설계하여 클린 코드와 확장성 높은 구조를 만들어보자!