팀스파르타 코딩

📘내일의 학습 목표: 인터페이스와 열거형

creator2041 2025. 4. 15. 19:26

🔹 01. 다중 상속을 사용하지 않는 이유

✅ C#은 다중 상속을 명시적으로 지원하지 않는 언어입니다. 그 이유는 다음과 같이 다양한 복잡성과 오류 가능성을 고려하여 의도적으로 제한하고 있기 때문입니다:

  1. 다이아몬드 문제(Diamond Problem):
    • 여러 부모 클래스로부터 같은 멤버(예: 같은 이름의 메서드)를 상속받을 경우, 어떤 부모의 것을 사용할지 모호해지는 상황이 발생합니다.
    • 이를 해결하기 위한 명시적 선언이나 우선순위 규칙은 코드의 가독성과 유지보수를 오히려 어렵게 만들 수 있습니다.
  2. 설계 복잡성 증가:
    • 다중 상속은 상속 트리를 복잡하게 만들어 전체 프로그램의 구조를 파악하기 어렵게 합니다.
    • 코드의 계층 구조가 얽히게 되면 디버깅, 수정, 협업이 더 까다로워집니다.
  3. 이름 충돌 및 재정의 위험:
    • 동일한 이름의 속성 또는 메서드가 여러 부모에 존재할 경우 충돌이 발생합니다.
    • 이를 해결하려면 자식 클래스에서 재정의(overriding)하거나 부모를 명시하는 등의 추가 작업이 필요합니다.
  4. 단순하고 명확한 구조를 유지하기 위함:
    • C#은 단일 상속과 다중 인터페이스 구현 방식을 선택함으로써, 클래스 간 계층 관계를 명확히 하고, 유지보수를 용이하게 합니다.
    • 복잡성을 인터페이스 분리를 통해 기능 단위로 나누는 것이 C#의 철학입니다.

🔹 02. 인터페이스를 사용하는 이유

✅ C#에서는 인터페이스(Interface)를 통해 다중 상속의 효과를 우회적으로 구현할 수 있습니다. 인터페이스는 다음과 같은 이유로 매우 유용합니다:

  1. 코드 재사용성 증가:
    • 여러 클래스가 공통 기능을 수행하도록 인터페이스를 정의해두면, 동일한 규격으로 다양한 클래스들을 일관되게 사용할 수 있습니다.
  2. 다중 기능 결합 가능:
    • 클래스는 여러 개의 인터페이스를 동시에 구현할 수 있으므로, 다중 역할을 하나의 객체에 부여할 수 있습니다.
  3. 유연하고 확장 가능한 설계 가능:
    • 클래스의 구체적인 구현과 관계없이 인터페이스로 기능을 제어하면, 유지보수와 확장성 면에서 훨씬 유리한 구조가 됩니다.

🔹 03. 인터페이스란?

✅ 개념 요약

정의 방식 메서드나 속성의 시그니처만 정의하고 구현은 하지 않음
구현 강제성 인터페이스를 상속받은 클래스는 모든 멤버를 반드시 구현해야 함
유연성 다중 구현 가능하며, 상속보다 느슨한 결합 제공

인터페이스는 '행동 규약' 또는 '기능의 명세'를 정의하며, 클래스에 특정 동작을 강제하는 일종의 틀로 사용됩니다.

쉽게 얘기해. USB포트는 여러 장치들을 호환하고 지원하게 하는데. 대충 그런 역할임.


✅ 예제 1 - 기본 문법 및 텍스트 RPG 명령어 시스템

인터페이스는 클래스가 따라야 할 규칙을 정의하는 구조이다. 예를 들어, 게임 내 명령어 시스템을 구현할 때, 모든 명령 클래스가 IMenuAction이라는 인터페이스를 구현하도록 강제함으로써 Execute()라는 메서드를 동일하게 가지게 만든다.

interface IMenuAction 
{
    void Execute();
}

class ShowStats : IMenuAction 
{
    public void Execute() 
    {
        Console.WriteLine("[스탯 보기 창 출력]");
    }
}

class UseItem : IMenuAction 
{
    public void Execute() 
    {
        Console.WriteLine("[아이템 사용 창 출력]");
    }
}

// 사용 예:
IMenuAction command = new ShowStats();
command.Execute();

🔧 사용 예 (텍스트 RPG): 콘솔에 출력되는 메뉴 선택지(예: '1. 스탯 보기', '2. 아이템 사용')에 따라 사용자가 선택한 기능을 실행할 때 각 명령어 클래스를 인터페이스로 구현해두면 명확하게 구조화할 수 있고, 유지보수도 수월하다.


✅ 예제 2 - 이동 가능한 객체 다형성

public interface IMovable 
{
    void Move(int x, int y);
}

public class Player : IMovable 
{
    public void Move(int x, int y) 
    {
        Console.WriteLine($"플레이어가 {x}, {y}로 이동!");
    }
}

public class Enemy : IMovable 
{
    public void Move(int x, int y) 
    {
        Console.WriteLine($"적이 {x}, {y}로 이동!");
    }
}

🔧 사용 예 (텍스트 RPG): '적이 다가온다!' 같은 프롬프트가 등장할 때, 플레이어와 적 모두 Move() 메서드를 통해 이동하도록 만든다. IMovable 인터페이스를 통해 다양한 객체에 동일한 이동 명령을 일관되게 적용할 수 있다.


🔹 04. 다중 인터페이스 구현

public interface IItemPickable 
{
    void PickUp();
}

public interface IDroppable 
{
    void Drop();
}

public class Item : IItemPickable, IDroppable 
{

    public string Name { get; set; }

    public void PickUp() 
    {
        Console.WriteLine($"아이템 {Name}을 주웠습니다.");
    }

    public void Drop() 
    {
        Console.WriteLine($"아이템 {Name}을 버렸습니다.");
    }
}

🔧 사용 예 (텍스트 RPG): 전투 후 획득한 아이템을 주울지 버릴지 선택할 수 있다. 예를 들어, item.PickUp() 또는 item.Drop() 메서드를 통해 동일한 아이템 객체에 다양한 동작을 부여할 수 있다.


🔹 05. 인터페이스 vs 추상 클래스 비교

비교 항목 인터페이스 추상 클래스

다중 상속 가능 불가능
구현 포함 불가능 가능 (일부 구현)
상속 제한 없음 단일 클래스만 상속 가능
사용 목적 기능의 규약 정의 공통 동작 구현 및 확장

🔎 요약: 인터페이스는 '이런 행동을 해야 해!' / 추상 클래스는 '이 기능은 공통으로 줄게!'로 기억하자.


🔹 06. 열거형(enum) 정리

열거형은 관련된 상수 집합을 정의하는 데 사용되는 자료형이다. 텍스트 기반 RPG에서 상태, 방향, 명령어 선택지 등을 표현할 때 매우 유용하게 활용된다. 특히 사용자 입력과 프로그램 내부 상태를 일치시키고 switch문과 함께 사용할 때 가독성이 매우 높아진다.

✅ 기본 개념

  • 관련된 상수 집합을 정의하는 데이터 타입
  • 내부적으로는 정수형 값을 가짐
  • switch 문에서 자주 사용됨

✅ GameState 예제

enum GameState 
{
    MainMenu,
    Playing,
    Paused,
    GameOver
}

GameState state = GameState.Playing;

switch (state) 
{
    case GameState.MainMenu:
        Console.WriteLine("메인 메뉴입니다.");
        break;
    case GameState.Playing:
        Console.WriteLine("게임이 진행 중입니다.");
        break;
    case GameState.Paused:
        Console.WriteLine("일시 정지 중입니다.");
        break;
    case GameState.GameOver:
        Console.WriteLine("게임 오버!");
        break;
}

🔧 사용 예 (텍스트 RPG): 플레이어의 현재 게임 상태를 추적하고 해당 상태에 따라 출력이나 기능을 달리할 수 있다. 예를 들어, 'Paused' 상태에서는 입력을 제한하고 'GameOver' 상태에서는 재시작 여부를 묻는 식으로 분기 처리가 가능하다.

✅ Direction 예제

enum Direction 
{
    Up,
    Down,
    Left,
    Right
}

Direction move = Direction.Left;

switch (move) 
{
    case Direction.Up:
        Console.WriteLine("위로 이동!");
        break;
    case Direction.Down:
        Console.WriteLine("아래로 이동!");
        break;
    case Direction.Left:
        Console.WriteLine("왼쪽으로 이동!");
        break;
    case Direction.Right:
        Console.WriteLine("오른쪽으로 이동!");
        break;
}

🔧 사용 예 (텍스트 RPG): 플레이어의 이동 방향을 enum으로 정의해두면, 사용자 입력을 쉽게 매핑하고 방향별 동작 로직을 명확하게 분기할 수 있다. 특히 맵 이동, 전투 회피, 탐색 등과 연결해 활용하기 좋다.

✅ ItemRarity 예제

enum ItemRarity 
{
    Common,
    Uncommon,
    Rare,
    Epic
}

ItemRarity rarity = ItemRarity.Epic;

switch (rarity) 
{
    case ItemRarity.Common:
        Console.WriteLine("일반 아이템입니다.");
        break;
    case ItemRarity.Uncommon:
        Console.WriteLine("고급 아이템입니다.");
        break;
    case ItemRarity.Rare:
        Console.WriteLine("희귀 아이템입니다!");
        break;
    case ItemRarity.Epic:
        Console.WriteLine("전설의 아이템!!");
        break;
}

🔧 사용 예 (텍스트 RPG): 드랍 아이템의 희귀도에 따라 출력 메시지나 플레이어의 반응, 심지어 전리품 효과를 달리 설정할 수 있다. 아이템 획득 시 사용자에게 가시적인 피드백을 주는 데 유용하다.


🧠 정리 및 회고

이번 인터페이스와 열거형 학습은 단순한 문법 공부를 넘어, 실제 게임 개발에 어떻게 쓰일 수 있는지를 체감하게 해주는 시간이었다. 과제를 하며 직접 텍스트 RPG의 구조를 고민할 때, 인터페이스를 통해 명령어를 추상화하거나 열거형으로 상태를 관리하는 것이 얼마나 강력한지 이해할 수 있었다.

처음에는 무작정 따라만 치던 코드들이 이제는 '아, 이건 상태를 표현하는 거구나', '이건 행동을 일관되게 정의하려는 거구나' 정도는 구별할 수 있게 된다. 아직 기능을 완벽히 구현할 수준은 아니지만, 적어도 코드가 대충 어떤 흐름으로 작동하는지는 눈에 들어온다. 이제야 코드라는 문장이 어렴풋이 읽히기 시작한다는 게, 이번 학습의 가장 큰 수확이다.