C# - 인터페이스

MothorMoth·2024년 4월 27일

C#

목록 보기
12/17
post-thumbnail

인터페이스 ( Interface )

  • 인터페이스는 클래스가 구현해야 하는 멤버들의 규격를 제공한다. 클래스가 인터페이스를 구현하는 경우 인터페이스의 모든 멤버를 구현해야하며, 다중 상속을 지원한다.

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

  1. 다이아몬드 문제(Diamond Problem): 다중 상속을 허용하면 한 클래스가 두 개 이상의 상위 클래스로부터 동일한 멤버를 상속받을 수 있다. 상위 클래스들이 같은 이름의 멤버를 가지고 있으면 어떤 상위 클래스의 멤버를 사용해야 하는지 모호해지고 이런 모호성을 해결하기 위해 규칙들을 설정하다 보면 코드가 복잡해지고 가독성이 저하될 수 있다.

  2. 설계의 복잡성 증가: 클래스가 다중 상속을 받을 경우, 어떤 클래스로부터 어떤 멤버를 상속받을지 결정해야 하고, 이로 인해 클래스 간의 상속 관계를 파악하기 어려워지고 코드의 유지 보수성이 저하될 수 있다.

  3. 이름 충돌과 충돌 해결의 어려움: 여러 상위 클래스로부터 상속받은 멤버들의 이름이 충돌하면, 이러한 충돌을 해결하기 위해 멤버를 재정의하거나 명시적으로 상위 클래스를 지정해야 할 수 있고, 이는 코드의 복잡성을 증가시키고 오류 발생 가능성을 높인다.

  4. 설계의 일관성과 단순성 유지: 단일 상속을 통해 클래스 간의 관계를 명확하게 만들고 코드의 가독성과 이해도를 높일 수 있다. 또한 인터페이스를 사용하여 다중 상속이 필요한 경우에도 유사한 기능을 구현할 수 있다.

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

  1. 코드의 재사용성: 다른 클래스에서 해당 인터페이스를 구현하면 동일한 기능을 공유하여 다양한 클래스가 동일한 동작을 수행할 수 있으므로 코드의 재사용성이 향상된다.

  2. 다중 상속 제공: C#에서 클래스는 단일 상속만을 지원하지만, 인터페이스를 통해 다중 상속을 모방할 수 있다. 인터페이스를 통해 클래스는 여러 기능을 조합하여 더 다양한 동작을 수행할 수 있다.

  3. 유연한 설계: 인터페이스를 사용하면 클래스와 인터페이스 간에 느슨한 결합을 형성할 수 있다. 클래스는 인터페이스를 구현하기만 하면 되므로, 클래스의 내부 구현에 대한 변경 없이 인터페이스의 동작을 변경하거나 새로운 인터페이스를 추가할 수 있다. 이는 유연하고 확장 가능한 소프트웨어 설계를 가능하게 한다.

3. 인터페이스 구현

  • 인터페이스에는 메소드, 프로퍼티, 이벤트, 인덱서 등이 포함될 수 있지만, 필드, 생성자, 정적 멤버는 포함될 수 없다. 다만, C# 8.0 이후부터는 정적 멤버를 포함할 수 있게 되었다.

  • 기본 인터페이스 구현

// 인터페이스 정의
public interface IMovable
{
    void Move(int x, int y); // Move 메서드 선언
}

// 인터페이스를 구현하는 클래스 생성
public class Player : IMovable
{
    public void Move(int x, int y) // Move 메서드 구현
    {
        // 플레이어의 이동 구현
    }
}

public class Enemy : IMovable
{
    public void Move(int x, int y) // Move 메서드 구현
    {
        // 적의 이동 구현
    }
}

// IMovable 타입의 객체 생성
IMovable movablePlayer = new Player();
IMovable movableEnemy = new Enemy();

movablePlayer.Move(5, 0);  // 플레이어 이동
movableEnemy.Move(1, 9); // 적 이동
  • 다중 인터페이스 구현
// 인터페이스 정의
public interface IMovable
{
    void Move(int x, int y);
}

public interface IAttackable
{
    void Attack();
}

// 인터페이스를 구현하는 클래스 생성
public class Player : IMovable, IAttackable
{
    public void Move(int x, int y)
    {
        // 플레이어의 이동 구현
    }

    public void Attack()
    {
        // 플레이어의 공격 구현
    }
}

// Player 타입의 객체 생성
Player player = new Player();

player.Move(5, 0);
player.Attack();
  • 인터페이스 캐스팅
// 인터페이스 정의
public interface IMovable
{
    void Move(int x, int y);
}

public interface IAttackable
{
    void Attack();
}

// 인터페이스를 구현하는 클래스 생성
public class Player : IMovable, IAttackable
{
    public void Move(int x, int y)
    {
        // 플레이어의 이동 구현
    }

    public void Attack()
    {
        // 플레이어의 공격 구현
    }
}

// IMovable 타입의 객체 생성
IMovable movablePlayer = new Player();
movablePlayer.Move(5, 0);

// IMovable 타입의 movablePlayer를 IAttackable 타입으로 캐스팅
IAttackable attackablePlayer = movablePlayer as IAttackable;
if (attackablePlayer != null)
{
    attackablePlayer.Attack();
}
  • 인터페이스 상속을 통한 기능 결합
    • 여러 기능을 항상 함께 사용해야 할 때 유용할 수 있다.
// 인터페이스 정의
public interface IMovable
{
    void Move(int x, int y);
}

public interface IAttackable
{
    void Attack();
}

public interface IActionable : IMovable, IAttackable
{
    // IActionable은 IMovable과 IAttackable의 메소드를 모두 상속받음
}

// 인터페이스를 구현하는 클래스 생성
public class Player : IActionable
{
    public void Move(int x, int y)
    {
        // 플레이어의 이동 구현
    }

    public void Attack()
    {
        // 플레이어의 공격 구현
    }
}

// IActionable 타입의 객체 생성
IActionable actionablePlayer = new Player();

actionablePlayer.Move(5, 0);
actionablePlayer.Attack();
  • 다중 상속 구현
// 인터페이스 정의
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}을 버렸습니다.");
    }
}

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

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

// Player, Item 클래스의 객체 생성
Player player = new Player();
Item item = new Item { Name = "Sword" };

player.InteractWithItem(item);
player.DropItem(item);

4. 인터페이스 vs 추상 클래스

인터페이스의 특징과 장단점

  • 인터페이스는 추상적인 동작만 정의하고, 구현을 갖지 않는다.

  • 다중 상속이 가능하며, 여러 클래스가 동일한 인터페이스를 구현할 수 있다.

  • 클래스들 간의 결합도를 낮추고, 유연한 상호작용을 가능하게 한다.

  • 코드의 재사용성과 확장성을 향상시킨다.

  • 단점으로는 인터페이스를 구현하는 클래스가 모든 동작을 구현해야 한다는 의무를 가지기 때문에 작업량이 증가할 수 있다.

추상 클래스의 특징과 장단점

  • 추상 클래스는 일부 동작의 구현을 가지며, 추상 메서드를 포함할 수 있다.

  • 단일 상속만 가능하며, 다른 클래스와 함께 상속 계층 구조를 형성할 수 있다.

  • 공통된 동작을 추상화하여 코드의 중복을 방지하고, 확장성을 제공한다.

  • 구현된 동작을 가지고 있기 때문에, 하위 클래스에서 재정의하지 않아도 될 경우 유용하다.

  • 단점으로는 다중 상속이 불가능하고, 상속을 통해 밀접하게 결합된 클래스들을 형성하므로 유연성이 제한될 수 있다.

0개의 댓글