Menu
Coddy logo textTech

인터페이스 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");
    }
}

주요 차이점은 상속 규칙과 목적에 있습니다.

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

서로 관련이 없는 클래스들이 동작을 공유해야 할 때 인터페이스를 사용하세요 (새와 비행기를 위한 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)을 추가하세요. 생성자는 제목, 기간, 아티스트를 인자로 받아야 합니다. 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 계약도 충족합니다. 추상 클래스는 관련된 미디어 유형에 대해 코드 재사용을 제공하고, 인터페이스는 상속 계층 구조를 공유하지 않고도 관련 없는 클래스가 "재생 가능"하도록 해줍니다.

치트 시트

인터페이스는 구현 없이 클래스가 따라야 하는 계약을 정의합니다:

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();  // Must be implemented
}

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

주요 차이점:

  • 클래스는 여러 인터페이스를 구현할 수 있지만, 하나의 추상 클래스만 상속받을 수 있습니다
  • 서로 관련 없는 클래스들이 동작을 공유해야 할 때 인터페이스를 사용하세요
  • 관련된 클래스들이 공통 코드와 상태를 공유할 때 추상 클래스를 사용하세요
  • 추상 클래스는 생성자, 필드, 접근 제한자를 가질 수 있습니다
  • 유연성을 위해 인터페이스를 선호하세요

직접 해보기

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실력 점검

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

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