본문 바로가기
Game DevTip/C#

4. C# 인터페이스 및 추상클래스 ,다형성

by LIKE IT.라이킷 2025. 4. 21.

 

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

댓글