C#은 기본적으로 다중 상속을 허용하지 않습니다.
복잡해지는 문제로 둘 이상의 클래스를 상속 받을 수 없도록 설정되어 있습니다.
흔히 죽음의 다이아몬드라고 하는데
상속된 기능을 사용할 때 어떤 클래스에서 상속 받은 건지 알 수 없는 혼란스러운 상황을 말합니다.
C#은 그래서 인터페이스라는 기능을 가지고 있습니다.
내부에 메서드가 있어 클래스와 비슷하지만 직접 구현하지는 않습니다.
인터페이스를 가지는 클래스에서 반드시 인터페이스의 정의를 구현해야 하는데 만들 수 밖에 없게 만들어 그 구성 요소들을 구현했다는 것을 보장할 수 있습니다.
즉, 내부에 구현을 강제할 메서드를 가지고, 이를 적용받은 클래스에서 구현하지 않으면 우리에게 잊고 구현하지 않았음을 상기시켜줍니다.
public interface IEnterable // 이름을 I로 시작하는 것이 규칙 (강제는 아님)
{
public void Enter();
}
public class Dungeon : IEnterable // IEnterable에 있는 메서드를 사용하지 않으면 오류!
{
public void Enter() // IEnterable의 메서드는 꼭 넣어야 한다
{
}
}
internal class Program
{
public interface IEnterable
{
public void Enter();
}
public interface IOpenable
{
public void Open();
}
public class Door : IEnterable, IOpenable // 클래스와 달리 2개의 인터페이스를 사용할 수 있음
{
public void Enter()
{
Console.WriteLine("문에 들어갑니다");
}
public void Open()
{
Console.WriteLine("문을 엽니다.");
}
}
public class Dungeon : IEnterable // 인터페이스에 있는 함수는 무조건 들어가야 한다!
{
public void Enter()
{
Console.WriteLine("던전에 들어갑니다");
}
}
public class Chest : IOpenable
{
public void Open()
{
Console.WriteLine("상자를 엽니다.");
}
}
public class player
{
public void Enter(IEnterable enterable)
{
enterable.Enter();
}
public void Open(IOpenable openable)
{
openable.Open();
}
}
static void Main(string[] args)
{
Chest chest = new Chest();
Dungeon dungeon = new Dungeon();
Door door = new Door();
player player = new player();
player.Enter(dungeon);
player.Open(chest);
// 인터페이스로 이중 상속을 할 수 있도록 한 경우
player.Enter(door);
player.Open(door);
}
}
}
위 코드를 보면 class Door 의 경우 2개의 인터페이스를 사용할 수 있는 것을 볼 수 있고 class Dungeon와 class Chest의 경우 각각 IEnterable에 있는 메서드 Enter와 IOpenable에 있는 메서드 Open이 필수로 들어가 있는 것을 알 수 있습니다!
저번에 언급했던 추상클래스와 인터페이스는 함수에 대한 선언만 정의하고 이를 포함하는 클래스에서 구체화하여 사용한다는 점에서 상당히 유사하다고 말할 수 있습니다.
하지만 여러가지 차이점이 있는데요 한번 살펴보겠습니다.
추상 클래스는 상속 관계인 경우에 사용합니다.
상속을 통해 얻을 수 있는 효과를 얻을 수 있고
부모클래스의 기능을 통해 자식클래스의 기능을 확장하는 경우 사용합니다.
부모클래스는 설계만하고 자식클래스가 구체화한다는 개념으로 이해하면 됩니다.
클래스가 해당 행동을 할 수 있는 경우에 사용합니다.
인터페이스에 정의된 함수들을 클래스의 목적에 맞게 기능을 구현하는 경우 사용합니다.
추상클래스와 달리 변수를 선언하는 것도, 함수를 정의하는 것이 불가능하고
다중 포함이 가능하다는 것이 차이점이라고 말할 수 있겠습니다.
public interface IEnterable
{
void Enter();
}
public abstract class Building : IEnterable
{
public void Enter()
{
}
}
// 은행은 건물범주에 포함되기 때문에 상속관계가 적합하다.
public class Bank : Building
{
}
// 차 안에 들어갈 수 있다는 점에서 행동 범주에 들어가 인터페이스가 적합하다.
public class Car : IEnterable
{
public void Enter()
{
}
}
}
위 코드를 보면 class Bank에 은행은 건물범주에 포함되기 때문에 상속관계가 적합하고
class Car는 차 안에 들어갈 수 있다는 점에서 행동 범주에 들어가 인터페이스가 적합합니다.