🔹 01. 다중 상속을 사용하지 않는 이유
✅ C#은 다중 상속을 명시적으로 지원하지 않는 언어입니다. 그 이유는 다음과 같이 다양한 복잡성과 오류 가능성을 고려하여 의도적으로 제한하고 있기 때문입니다:
- 다이아몬드 문제(Diamond Problem):
- 여러 부모 클래스로부터 같은 멤버(예: 같은 이름의 메서드)를 상속받을 경우, 어떤 부모의 것을 사용할지 모호해지는 상황이 발생합니다.
- 이를 해결하기 위한 명시적 선언이나 우선순위 규칙은 코드의 가독성과 유지보수를 오히려 어렵게 만들 수 있습니다.
- 설계 복잡성 증가:
- 다중 상속은 상속 트리를 복잡하게 만들어 전체 프로그램의 구조를 파악하기 어렵게 합니다.
- 코드의 계층 구조가 얽히게 되면 디버깅, 수정, 협업이 더 까다로워집니다.
- 이름 충돌 및 재정의 위험:
- 동일한 이름의 속성 또는 메서드가 여러 부모에 존재할 경우 충돌이 발생합니다.
- 이를 해결하려면 자식 클래스에서 재정의(overriding)하거나 부모를 명시하는 등의 추가 작업이 필요합니다.
- 단순하고 명확한 구조를 유지하기 위함:
- C#은 단일 상속과 다중 인터페이스 구현 방식을 선택함으로써, 클래스 간 계층 관계를 명확히 하고, 유지보수를 용이하게 합니다.
- 복잡성을 인터페이스 분리를 통해 기능 단위로 나누는 것이 C#의 철학입니다.
🔹 02. 인터페이스를 사용하는 이유
✅ C#에서는 인터페이스(Interface)를 통해 다중 상속의 효과를 우회적으로 구현할 수 있습니다. 인터페이스는 다음과 같은 이유로 매우 유용합니다:
- 코드 재사용성 증가:
- 여러 클래스가 공통 기능을 수행하도록 인터페이스를 정의해두면, 동일한 규격으로 다양한 클래스들을 일관되게 사용할 수 있습니다.
- 다중 기능 결합 가능:
- 클래스는 여러 개의 인터페이스를 동시에 구현할 수 있으므로, 다중 역할을 하나의 객체에 부여할 수 있습니다.
- 유연하고 확장 가능한 설계 가능:
- 클래스의 구체적인 구현과 관계없이 인터페이스로 기능을 제어하면, 유지보수와 확장성 면에서 훨씬 유리한 구조가 됩니다.
🔹 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의 구조를 고민할 때, 인터페이스를 통해 명령어를 추상화하거나 열거형으로 상태를 관리하는 것이 얼마나 강력한지 이해할 수 있었다.
처음에는 무작정 따라만 치던 코드들이 이제는 '아, 이건 상태를 표현하는 거구나', '이건 행동을 일관되게 정의하려는 거구나' 정도는 구별할 수 있게 된다. 아직 기능을 완벽히 구현할 수준은 아니지만, 적어도 코드가 대충 어떤 흐름으로 작동하는지는 눈에 들어온다. 이제야 코드라는 문장이 어렴풋이 읽히기 시작한다는 게, 이번 학습의 가장 큰 수확이다.
'팀스파르타 코딩' 카테고리의 다른 글
| 📚 복습 정리 - 텍스트 RPG 개발에 사용된 C# 개념들 (개념 + 실습 응용) (0) | 2025.04.16 |
|---|---|
| 스파르타 던전 프로젝트 기능 설명 및 트러블 슈팅 (0) | 2025.04.16 |
| 🧠2025년 4월 15일 (월) / C# 객체지향 심화용 - 제너릭과 out, ref 키워드 (0) | 2025.04.15 |
| 📚 TIL - 2025년 4월 15일 (월) / C# 객체지향 기본 (0) | 2025.04.15 |
| 📚 내일의 학습 목표 - 클래스와 객체 (C#) (0) | 2025.04.14 |