Menu
CoddyTech

인터페이스 vs 추상 클래스

Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 70개 중 26번째.

인터페이스와 추상 클래스 모두 다른 클래스가 따라야 하는 계약을 정의할 수 있지만, 서로 다른 목적을 가지며 각기 다른 기능을 제공합니다.

인터페이스는 구현 없이 클래스가 무엇을 해야 하는지 정의합니다. 이는 순수한 계약입니다:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

추상 클래스는 계약과 공유 구현을 모두 제공할 수 있습니다. 이는 부분적인 설계도입니다:

public abstract class Vehicle
{
    public string Brand { get; set; }
    
    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
    
    public abstract void Drive();  // 반드시 구현해야 함
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving on roads");
    }
}

핵심적인 차이는 상속 규칙과 목적에 있습니다.

클래스는 여러 인터페이스를 구현할 수 있지만, 추상 클래스는 하나만 상속할 수 있습니다.

클래스는 추상 클래스를 상속하면서 동시에 인터페이스를 구현할 수도 있습니다. 콜론 뒤에는 기본 클래스가 먼저 오고, 그다음 쉼표로 구분된 인터페이스가 옵니다.

public class Airplane : Vehicle, IFlyable
{
    public override void Drive()
    {
        Console.WriteLine("Taxiing to the runway");
    }

    public void Fly()
    {
        Console.WriteLine("Flying above the clouds");
    }
}

관련 없는 클래스들이 동작을 공유해야 할 때는 인터페이스를 사용하세요(예: 새와 비행기를 위한 IFlyable). 관련 클래스들이 공통 코드와 상태를 공유할 때는 추상 클래스를 사용하세요(예: 차량들이 엔진 시동 메커니즘을 공유하는 경우).

추상 클래스에는 생성자, 필드 및 멤버의 액세스 한정자가 있을 수 있습니다. 인터페이스에는 전통적으로 이러한 요소를 사용할 수 없었지만, 최신 C#에서는 기본 구현을 허용합니다.

확실하지 않다면 유연성을 위해 인터페이스를 우선 사용하세요. 공통 구현이 필요해지면 나중에 추상 클래스를 추가할 수 있습니다.

challenge icon

챌린지

쉬움

인터페이스와 추상 클래스 중 무엇을 언제 사용해야 하는지 보여주는 미디어 플레이어 시스템을 만들어 봅시다. 서로 관련이 없는 장치들은 인터페이스를 통해 재생 동작을 공유하고, 서로 관련된 미디어 유형들은 추상 클래스를 통해 공통 구현을 공유하는 설계를 만들겠습니다.

코드를 네 개의 파일에 나누어 작성합니다.

  • IPlayable.cs: Media 네임스페이스에 IPlayable이라는 인터페이스를 정의합니다. 이 인터페이스는 휴대폰, TV, 전용 음악 플레이어 등 미디어를 재생할 수 있는 모든 대상이 따라야 하는 규약을 나타냅니다. 문자열을 반환하는 단일 메서드 Play()를 선언해야 합니다.
  • MediaFile.cs: Media 네임스페이스에 MediaFile이라는 추상 클래스를 정의합니다. 이 클래스는 모든 미디어 파일의 공통 기반을 나타냅니다. Title 속성(string)과 Duration 속성(int, 초를 나타냄)을 가져야 합니다. 두 값을 설정하는 생성자를 포함합니다. "{Title} ({Duration}s)"를 반환하는 구체 메서드 GetInfo()를 추가합니다. 또한 문자열을 반환하는 추상 메서드 GetType()을 선언합니다. 각 미디어 유형은 어떤 종류의 파일인지 정의합니다.
  • AudioFile.cs: Media 네임스페이스에 MediaFile을 상속하고 IPlayable을 구현하는 AudioFile 클래스를 정의합니다. Artist 속성(string)을 추가합니다. 생성자는 title, duration, artist를 받아야 합니다. GetType()을 재정의하여 "Audio"를 반환하도록 합니다. Play()를 구현하여 "Playing audio: {Title} by {Artist}"를 반환하도록 합니다.
  • Program.cs: 메인 파일에서 입력값을 사용해 AudioFile을 만듭니다. 클래스의 두 가지 측면을 보여 줍니다. 추상 클래스에서 상속된 GetInfo()와 GetType()을 호출한 다음, 인터페이스의 Play()를 호출합니다. 오디오 파일을 IPlayable 변수에 저장하고 Play()를 다시 호출하여 인터페이스 다형성을 보여 줍니다.

입력은 세 개입니다.

  • 곡 제목
  • 초 단위 재생 시간
  • 아티스트 이름

다음 형식으로 출력합니다.

Info: {GetInfo() result}
Type: {GetType() result}
Direct: {Play() result}
Via Interface: {Play() result from IPlayable variable}

예를 들어 입력이 Bohemian Rhapsody, 354, Queen이라면 출력은 다음과 같아야 합니다.

Info: Bohemian Rhapsody (354s)
Type: Audio
Direct: Playing audio: Bohemian Rhapsody by Queen
Via Interface: Playing audio: Bohemian Rhapsody by Queen

AudioFile은 두 접근 방식의 이점을 모두 얻습니다. 추상 클래스에서 공통 구현(GetInfo())을 상속받으면서 IPlayable 규약도 충족합니다. 추상 클래스는 관련 미디어 유형 간의 코드 재사용을 제공하고, 인터페이스는 상속 계층을 공유하지 않는 클래스도 무엇이든 "재생 가능"하게 해 줍니다.

직접 해보기

using System;
using Media;

class Program
{
    public static void Main(string[] args)
    {
        // 입력값 읽기
        string title = Console.ReadLine();
        int duration = Convert.ToInt32(Console.ReadLine());
        string artist = Console.ReadLine();
        
        // TODO: 입력값을 사용하여 AudioFile 생성
        
        // TODO: Info:를 출력한 다음 GetInfo()의 결과 출력
        
        // TODO: Type:을 출력한 다음 GetType()의 결과 출력
        
        // TODO: Direct:를 출력한 다음 Play()의 결과 출력
        
        // TODO: 오디오 파일을 IPlayable 변수에 저장
        
        // TODO: Via Interface:을 출력한 다음 IPlayable 변수의 Play() 결과 출력
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 C# 컴파일러