24.01.16 TIL - [C#] 기초 (11) : 인터페이스 & 열거형

JJwoo·2024년 1월 16일
0

C#

목록 보기
11/20

✔ 다중 상속을 사용하지 않는 이유

설계의 일관성과 단순성 유지:

  • 다이아몬드 문제(Diamond Problem): 다중 상속을 허용하면 한 클래스가 두 개 이상의 부모 클래스로부터 동일한 멤버를 상속받을 수 있고, 이로 인해 어떤 부모 클래스의 멤버를 사용해야 하는지 모호해집니다.

  • 설계의 복잡성 증가: 다중 상속을 허용하면 클래스 간의 관계가 복잡해지며, 어떤 클래스로부터 어떤 멤버를 상속받을지 결정하기 어렵고 코드의 유지 보수성이 저하될 수 있습니다.

  • 이름 충돌과 충돌 해결의 어려움: 다중 상속을 사용하면 여러 부모 클래스로부터 상속받은 멤버들이 이름 충돌할 수 있으며, 이를 해결하기 위해 복잡한 재정의나 명시적인 부모 클래스 지정이 필요할 수 있습니다.

  • 설계의 일관성과 단순성 유지: C#은 단일 상속을 선호하여 설계의 일관성과 단순성을 유지하고, 인터페이스를 통해 다중 상속이 필요한 경우 유사한 기능을 구현할 수 있도록 합니다. 이를 통해 코드의 가독성과 이해도를 높일 수 있습니다.


🌟인터페이스 (Interface)

1. 인터페이스 개념

인터페이스는 클래스나 구조체가 구현해야 할 메서드, 프로퍼티, 이벤트, 인덱서의 명세를 정의합니다.

구체적인 기능 구현은 제공하지 않으며, 계약과 같은 역할을 합니다.

2. 인터페이스의 특징

  • 메서드 시그니처만 제공: 구체적인 구현은 포함하지 않고, 어떤 메서드와 프로퍼티가 필요한지만 명시합니다.

  • 다중 상속 지원: 한 클래스가 여러 인터페이스를 구현할 수 있습니다.

  • 다형성 구현: 서로 다른 클래스들이 같은 인터페이스를 구현함으로써 다형성을 달성할 수 있습니다.

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

  • 다중 상속 대체: 다중 상속을 대체하여 클래스에 여러 개의 기능을 추가 및 확장 가능.

  • 계약 정의: 클래스가 반드시 따라야 하는 동작 계약을 명시하고 강제화.
    다형성과 추상화: 다형성을 촉진하고 추상화를 구현하여 유연한 객체 지향 프로그래밍 가능.

  • 코드 재사용과 모듈화: 표준화된 기능을 여러 클래스에서 공유 및 재사용 가능.

  • 유연한 설계와 확장성: 변경 및 확장이 쉬우며 유연한 설계 제공.

  • 단위 테스트와 모의 객체 생성: 테스트 및 모의 객체 생성 용이성 제공.

아이템 사용구현 예제

// 아이템을 사용할 수 있는 인터페이스
public interface IUsable
{
    void Use();
}

// 아이템 클래스
public class Item : IUsable
{
    public string Name { get; set; }

    public void Use()
    {
        Console.WriteLine("아이템 {0}을 사용했습니다.", Name);
    }
}

// 플레이어 클래스
public class Player
{
    public void UseItem(IUsable item)
    {
        item.Use();
    }
}

// 게임 실행
static void Main()
{
    Player player = new Player();
    Item item = new Item { Name = "Health Potion" };
    player.UseItem(item);
}

다중상속 예시

// 인터페이스 1
public interface IItemPickable
{
    void PickUp();
}

// 인터페이스 2
public interface IDroppable
{
    void Drop();
}

// 아이템 클래스
public class Item : IItemPickable, IDroppable
{
    public string Name { get; set; }

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

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

// 플레이어 클래스
public class Player
{
    public void InteractWithItem(IItemPickable item)
    {
        item.PickUp();
    }

    public void DropItem(IDroppable item)
    {
        item.Drop();
    }
}

// 게임 실행
static void Main()
{
    Player player = new Player();
    Item item = new Item { Name = "Sword" };

    // 아이템 주울 수 있음
    player.InteractWithItem(item);

    // 아이템 버릴 수 있음
    player.DropItem(item);
}

--

활용 예시 3


// 인터페이스 정의: IAttackable

public interface IAttackable
{
    void Attack();
}

// 게임 캐릭터 클래스 1: Warrior (전사)
public class Warrior : IAttackable
{
    public void Attack()
    {
        Console.WriteLine("전사가 검으로 공격합니다!");
    }
}

// 게임 캐릭터 클래스 2: Wizard (마법사)
public class Wizard : IAttackable
{
    public void Attack()
    {
        Console.WriteLine("마법사가 주문으로 공격합니다!");
    }
}

// 게임 캐릭터 클래스 3: Archer (궁수)
public class Archer : IAttackable
{
    public void Attack()
    {
        Console.WriteLine("궁수가 활로 공격합니다!");
    }
}

class Program
{
    static void Main()
    {
        // 다양한 캐릭터 생성 및 공격
        IAttackable character1 = new Warrior();
        IAttackable character2 = new Wizard();
        IAttackable character3 = new Archer();

        character1.Attack(); // 전사가 검으로 공격합니다!
        character2.Attack(); // 마법사가 주문으로 공격합니다!
        character3.Attack(); // 궁수가 활로 공격합니다!
    }
}


이렇게 인터페이스를 사용하면 다양한 캐릭터나 게임 요소가 동일한 인터페이스를 준수하여 일관된 방식으로 상호 작용할 수 있습니다.


🌟열거형 (Enumeration)

1. 열거형 개념

열거형은 관련된 상수들을 함께 그룹화하여 타입 안전한 방법으로 정의할 수 있게 해줍니다. 가독성과 유지 보수성을 높이는 데 유용합니다.

2. 열거형의 특징

  • 타입 안전성 제공: 열거형을 사용하면 정수와 같은 원시 값 대신 명확한 이름을 사용할 수 있습니다.

  • 코드의 가독성 향상: 코드 내에서 상수의 의미를 쉽게 이해할 수 있습니다.

3. 열거형을 사용하는 이유

  • 의미 있는 이름: enum은 상수 값을 나타내는데 의미 있는 이름을 사용합니다.

  • 가독성 향상: 코드의 가독성을 높이고 이해하기 쉽게 합니다.

  • 값의 범위 제한: 잘못된 값 사용을 방지하고 유효한 값만 허용합니다.

  • 코드 유지보수 용이성: 요구 사항 변경 시 enum 값만 수정하여 코드 변경을 간편하게 합니다.

  • 타입 안정성: enum 값은 타입 안전하게 사용되며 혼동을 방지합니다.

열거형 예문


Copy code
public enum Day
{
    Sunday,
    Monday,
    Tuesday,
   
Wednesday,
Thursday,
Friday,
Saturday
}

public class Test
{
public Day CurrentDay { get; set; }

sql
Copy code
public Test(Day day)
{
    CurrentDay = day;
}
}

Test test = new Test(Day.Monday);

yaml
Copy code
이 예제에서 `Day` 열거형은 요일을 나타내며, `Test` 클래스는 `Day` 열거형을 사용하여 현재 요일을 저장합니다.
// 게임 상태
enum GameState
{
    MainMenu,
    Playing,
    Paused,
    GameOver
}

// 방향
enum Direction
{
    Up,
    Down,
    Left,
    Right
}

// 아이템 등급
enum ItemRarity
{
    Common,
    Uncommon,
    Rare,
    Epic
}

예제2)

enum DaysOfWeek
{
    Monday,    // 0
    Tuesday,   // 1
    Wednesday, // 2
    Thursday,  // 3
    Friday,    // 4
    Saturday,  // 5
    Sunday     // 6
}

class Program
{
    static void Main()
    {
        // 열거형 상수 사용
        DaysOfWeek today = DaysOfWeek.Wednesday;

        // 출력
        Console.WriteLine("오늘은 " + today + "입니다.");
    }
}

위의 코드에서 DaysOfWeek 열거형을 사용하여 요일을 나타내고, today 변수에 현재 요일을 저장하고 출력합니다. 

Enum은 코드의 가독성을 향상시키고 유지보수를 용이하게 만들어줍니다.

💥 클래스와 인터페이스의 다중 상속 차이

특성클래스의 다중 상속인터페이스의 다중 상속
다중 상속 허용 여부허용 x허용함
상속 대상클래스로부터 다중 상속을 받음인터페이스로부터 다중 상속을 받음
멤버 상속모든 멤버(필드, 메서드 등)를 상속메서드 시그니처만 상속
모호성 해결다이아몬드 문제 발생 가능모호성 없음
코드 재사용과 모듈화코드 재사용과 모듈화에 활용 가능코드 재사용과 모듈화에 주로 활용
특별한 예외 처리 필요예외 상황에서 명시적인 규칙 필요명시적인 규칙 없음
설계 유연성설계 복잡성과 관련된 문제 발생 가능유연하고 단순한 설계 가능
C#에서의 지원C#에서는 다중 클래스 상속 허용 안 함C#에서는 인터페이스 다중 상속 허용함

1) 클래스의 다중 상속을 추천할 때:

  • 여러 클래스가 공통된 구현을 가지며, 이러한 구현을 코드 중복 없이 재사용하려는 경우.

  • 직접 상속받는 클래스들 간에 구현이 겹치거나 중복되지 않을 때.

2) 인터페이스를 사용하는 것이 더 나을 때:

  • 여러 클래스가 공통된 동작을 정의하되, 구현이 다를 때. (다중 상속을 사용하면 모호성 문제가 발생할 수 있으므로 인터페이스가 더 적합)

  • 클래스가 이미 다른 클래스를 상속하고 있어 클래스의 다중 상속이 불가능한 경우. (C#에서는 클래스의 다중 상속을 지원하지 않음)

클래스 상속 예시) 게임 캐릭터의 직업을 나타내는 클래스를 고려

class Character
{
    public string Name { get; set; }
    public int Level { get; set; }
    public int MaxHP { get; set; }
    // 공통 속성 및 동작...
}

class Warrior : Character
{
    // 전사 직업 특성 및 동작...
}

class Wizard : Character
{
    // 마법사 직업 특성 및 동작...
}

하지만 만약 각 직업 클래스가 공통된 동작만 정의하고 직업 특성이 매우 다른 경우, 인터페이스를 사용하여 직업 특성을 분리하고 각 직업에 필요한 인터페이스를 구현하는 것이 더 나을 수 있다.

종합적으로, 다중 상속을 사용할 때는 주의가 필요하며 클래스 간의 관계와 요구 사항을 고려하여 인터페이스 또는 클래스 상속을 선택하는 것이 중요.


💎 다이아몬드 문제

다이아몬드 문제(Diamond Problem)는 객체 지향 프로그래밍 언어에서 다중 상속을 지원할 때 발생할 수 있는 문제 중 하나입니다.

  • 다이아몬드 상속: 다이아몬드 상속은 한 클래스가 두 개 이상의 클래스로부터 상속을 받을 때, 두 개 이상의 부모 클래스가 같은 조상 클래스(또는 인터페이스)를 가질 때 발생합니다. 이런 상황에서 자식 클래스가 조상 클래스의 멤버를 호출하거나 멤버 충돌이 발생할 수 있습니다.
using System;

interface IA
{
    void MethodA();
}

interface IB
{
    void MethodB();
}

class MyBaseClass
{
    public void CommonMethod()
    {
        Console.WriteLine("Base class's common method");
    }
}

class MyClass : IA, IB
{
    public void MethodA()
    {
        Console.WriteLine("MethodA in MyClass");
    }

    public void MethodB()
    {
        Console.WriteLine("MethodB in MyClass");
    }
}

class MyDerivedClass : MyClass
{
    public void MyMethod()
    {
        Console.WriteLine("MyMethod in MyDerivedClass");
    }
}

class Program
{
    static void Main()
    {
        MyDerivedClass obj = new MyDerivedClass();
        obj.MethodA();         // 호출 결과: MethodA in MyClass
        obj.MethodB();         // 호출 결과: MethodB in MyClass
        obj.CommonMethod();    // 호출 결과: Base class's common method
        obj.MyMethod();        // 호출 결과: MyMethod in MyDerivedClass
    }
}

위의 코드에서 MyClass 클래스는 IA와 IB 인터페이스를 구현하고, MyDerivedClass 클래스는 MyClass를 상속받습니다. MyClass에서 구현한 MethodA와 MethodB 메서드가 MyDerivedClass에서 호출될 수 있으며, CommonMethod는 MyDerivedClass에서도 호출 가능합니다.

다이아몬드 문제가 발생하는 경우, 상속 계층 구조가 복잡해지면서 어떤 메서드나 속성을 호출해야 할지 모호성이 발생하거나 충돌이 발생할 수 있으므로 주의해야 합니다.

이러한 문제를 피하기 위해 C#은 다중 클래스 상속을 허용하지 않고, 인터페이스를 통한 다중 상속을 지원합니다.


profile
개발 모코코

0개의 댓글