C#의 인터페이스와 추상 클래스, 그리고 다형성의 활용
C#에서는 클래스 간의 관계를 정의하거나 특정 기능을 보장하기 위해 **추상 클래스(Abstract Class)**와 **인터페이스(Interface)**를 사용한다. 이 글에서는 RPG 전투 시뮬레이션을 예시로 하여, 이 두 개념과 더불어 **다형성(Polymorphism)**을 어떻게 활용할 수 있는지 설명한다.
1. 인터페이스란?
인터페이스는 클래스가 따라야 할 **명세(contract)**를 정의한다. 구현을 포함하지 않으며, 오직 시그니처만을 포함한다. 이를 통해 클래스가 특정 기능을 반드시 제공하도록 강제할 수 있다.
interface ISpecialAbility
{
void UseSpecial(Character target);
}
ISpecialAbility 인터페이스는 UseSpecial 메서드를 반드시 구현해야 함을 의미하며, 실제 동작은 이를 구현한 클래스에서 정의해야 한다.
2. 추상 클래스란?
추상 클래스는 공통 기능과 틀을 제공하는 데 사용된다. 일부 메서드를 구현할 수도 있고, 일부는 추상 메서드로 남겨 하위 클래스가 반드시 구현하도록 강제할 수 있다.
abstract class Character
{
public string Name { get; set; }
public int Level { get; set; }
public int HP { get; set; }
public Character(string name, int level, int hp) { ... }
public virtual void Attack(Character target)
{
Console.WriteLine($"{Name}이(가) 공격합니다!");
}
}
3. 다형성의 활용
다형성은 여러 클래스가 동일한 인터페이스나 기반 클래스를 공유하면서, 각자 다른 방식으로 동작을 구현하는 객체 지향 원리다.
예를 들어 Character 클래스를 기반으로 한 Hero와 Monster 클래스는 Attack 메서드를 각각 다르게 구현할 수 있다.
public override void Attack(Character target)
{
Console.WriteLine($"{Name}이(가) 강력한 검으로 공격!");
...
}
다형성을 통해 Character 타입으로 모든 객체를 다룰 수 있으면서도, 실제 동작은 해당 클래스의 구현에 따라 다르게 실행된다.
4. 예제 요약
- Hero는 Character를 상속하고, ISpecialAbility를 구현한다.
- Monster는 Character를 상속하며, 자체 방어력 속성을 가진다.
- 전투 시 Hero는 일반 공격과 특수 능력(Fireball)을 통해 Monster를 공격한다.
- Monster는 Hero를 반격하며, HP 및 상태 변화가 출력된다.
Hero hero = new Hero("용사", 10, 100, 50);
Monster monster = new Monster("고블린", 5, 50, 5);
hero.Attack(monster); // 일반 공격
hero.UseSpecial(monster); // 특수 능력 사용
monster.Attack(hero); // 반격
5. 자동 구현 프로퍼티
C#에서는 필드를 자동으로 생성하고 관리할 수 있도록 자동 구현 프로퍼티 문법을 제공한다.
public int Defense { get; set; }
이는 다음과 같은 코드와 동일한 효과를 갖는다:
private int _defense;
public int Defense
{
get { return _defense; }
set { _defense = value; }
}
6. 인터페이스 vs 추상 클래스
항목추상 클래스인터페이스
상속/구현 수 | 단일 상속 | 다중 구현 |
구현 포함 | 일부 가능 | 일반적으로 없음 |
목적 | 공통 기능 제공 | 기능 명세 제공 |
사용 용도 | 비슷한 객체들의 공통 코드 정의 | 기능 보장을 위한 계약 정의 |
결론
인터페이스와 추상 클래스는 클래스 간의 책임과 구조를 명확히 구분하고, 유연한 코드 구성을 가능하게 한다. 이를 통해 유지보수성과 확장성이 뛰어난 구조를 만들 수 있다. 특히, 다형성과 결합되면 동일한 타입으로 다양한 객체를 유연하게 처리할 수 있어 실무에서 매우 중요한 개념이다.
[요구사항]
- 상속:
- 기본 클래스 Character를 정의하고, Hero와 Monster가 상속받음.
- 각 하위 클래스는 고유 속성 또는 메서드 추가.
- 다형성:
- Character 타입으로 Hero와 Monster를 다루며, 오버라이딩으로 동작 재정의.
- 인터페이스:
- ISpecialAbility 인터페이스를 정의하고, Hero class에 특수 능력 부여.
- 기능:
- 캐릭터와 몬스터 생성.
- 전투 시뮬레이션 (공격, 특수 능력 사용).
- 상태 출력 및 다형성 테스트.
- [ISpecialAbility 인터페이스 예시]
interface ISpecialAbility
{
void UseSpecial(Character target);
}
[실행 예시]
RPG 전투 시작!
[영웅] 이름: 용사, 레벨: 10, HP: 100, Mana: 50
이름: 고블린, 레벨: 5, HP: 50
용사이(가) 강력한 검으로 고블린을(를) 공격! 70 데미지
고블린이(가) 방어력 5로 65 데미지를 막음
이름: 고블린, 레벨: 5, HP: 0
용사이(가) 특수 능력 '파이어볼'을 사용! 100 데미지
이름: 고블린, 레벨: 5, HP: 0
고블린이(가) 용사를(를) 물어뜯음! 15 데미지
[영웅] 이름: 용사, 레벨: 10, HP: 85, Mana: 30
[code]
using System;
namespace RPGSimulation
{
// 인터페이스: 특수 능력 사용 정의
interface ISpecialAbility
{
void UseSpecial(Character target);
}
// 기본 클래스: Character
abstract class Character
{
public string Name { get; set; }
public int Level { get; set; }
public int HP { get; set; }
public Character(string name, int level, int hp)
{
Name = name;
Level = level;
HP = hp;
}
// 기본 공격 메서드 (다형성: 하위 클래스에서 재정의)
public virtual void Attack(Character target)
{
Console.WriteLine($"{Name}이(가) 공격합니다!");
}
public override string ToString()
{
return $"이름: {Name}, 레벨: {Level}, HP: {HP}";
}
}
// 하위 클래스: Hero (ISpecialAbility 인터페이스 구현)
class Hero : Character, ISpecialAbility
{
public int Mana { get; set; }
public Hero(string name, int level, int hp, int mana)
: base(name, level, hp)
{
Mana = mana;
}
// 공격 메서드 오버라이딩: 몬스터의 방어력 고려
public override void Attack(Character target)
{
int damage = 70;
Console.WriteLine($"{Name}이(가) 강력한 검으로 {target.Name}을(를) 공격! {damage} 데미지");
if (target is Monster monster)
{
int defense = monster.Defense;
int actualDamage = damage - defense;
if(actualDamage < 0)
actualDamage = 0;
Console.WriteLine($"{monster.Name}이(가) 방어력 {defense}로 {actualDamage} 데미지를 막음");
monster.HP -= actualDamage;
if(monster.HP < 0)
monster.HP = 0;
}
else
{
target.HP -= damage;
if(target.HP < 0)
target.HP = 0;
}
}
// 특수 능력 사용: 파이어볼 (100 데미지)
public void UseSpecial(Character target)
{
int specialDamage = 100;
Console.WriteLine($"{Name}이(가) 특수 능력 '파이어볼'을 사용! {specialDamage} 데미지");
target.HP -= specialDamage;
if(target.HP < 0)
target.HP = 0;
}
public override string ToString()
{
return $"[영웅] 이름: {Name}, 레벨: {Level}, HP: {HP}, Mana: {Mana}";
}
}
// 하위 클래스: Monster
class Monster : Character
{
public int Defense { get; set; }
public Monster(string name, int level, int hp, int defense)
: base(name, level, hp)
{
Defense = defense;
}
// 몬스터 공격 메서드 오버라이딩
public override void Attack(Character target)
{
int damage = 15;
Console.WriteLine($"{Name}이(가) {target.Name}을(를) 물어뜯음! {damage} 데미지");
target.HP -= damage;
if(target.HP < 0)
target.HP = 0;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("RPG 전투 시작!");
// 캐릭터 생성
Hero hero = new Hero("용사", 10, 100, 50);
Monster monster = new Monster("고블린", 5, 50, 5);
// 상태 출력
Console.WriteLine(hero.ToString());
Console.WriteLine(monster.ToString());
// 전투 시뮬레이션
// 1. 영웅이 몬스터 공격
hero.Attack(monster);
Console.WriteLine(monster.ToString());
// 2. 영웅의 특수 능력 사용
hero.UseSpecial(monster);
Console.WriteLine(monster.ToString());
// 3. 몬스터가 영웅 공격
monster.Attack(hero);
Console.WriteLine(hero.ToString());
}
}
}
'Game DevTip > C#' 카테고리의 다른 글
3. C# 클래스 (0) | 2025.04.21 |
---|---|
2. C# 배열, 함수, 델리게이트 (0) | 2025.04.21 |
1. C# 기초 (0) | 2025.04.21 |
댓글