Menu
Coddy logo textTech

インターフェース vs 抽象クラス

CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 26/70。

interface と abstract class は、他の class が従う必要のある契約を定義できますが、それぞれ異なる目的を持ち、利用できる機能も異なります。

interface は、実装を一切持たずに、クラスが何をしなければならないかを定義します。純粋な契約です:

public interface IFlyable
{
    void Fly();
}

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

abstract classは、契約と共有実装の両方を提供できます。これは部分的な設計図です:

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");
    }
}

主な違いは、継承のルールと目的にあります。

class は複数のインターフェースを実装できますが、1つの abstract class からのみ継承できます。

関連のない class が動作を共有する必要がある場合(鳥や飛行機の IFlyable のようなケース)は interface を使用します。関連する class が共通のコードと状態を共有する場合(車両がエンジンの始動メカニズムを共有するようなケース)は abstract class を使用します。

抽象クラスには、メンバーのコンストラクター、フィールド、アクセス修飾子を定義できます。従来、インターフェイスにはこれらを定義できませんでしたが、現代の C# ではデフォルト実装が可能です。

迷ったときは、柔軟性のために interface を優先しましょう。共有実装が必要になった場合でも、後から abstract class を追加できます。

challenge icon

チャレンジ

簡単

interface と abstract class のどちらを使うべきかを示すメディアプレーヤーシステムを構築しましょう。関連のないデバイスが interface を通じて再生動作を共有し、関連するメディア型が abstract class を通じて共通の実装を共有する設計を作成します。

コードを4つのファイルに分けて整理します。

  • IPlayable.csMedia namespace に IPlayable という interface を Define します。この interface は、phone、TV、専用の music player など、メディアを再生できるあらゆるものの contract を表します。string を返す単一の method Play() を declare します。
  • MediaFile.csMedia namespace に MediaFile という abstract class を Define します。これはすべての media file に共通する基盤を表します。Title property(string)と、Duration property(秒数を表す int)を持たせます。両方の値を設定する constructor を含めます。"{Title} ({Duration}s)" を返す concrete method GetInfo() を追加します。また、string を返す abstract method GetType() も declare します。各メディア型が、それがどの種類の file なのかを Define します。
  • AudioFile.csMedia namespace に AudioFile class を Define し、MediaFile から Inherits して IPlayable を Implements します。Artist property(string)を追加します。constructor は title、duration、artist を受け取るようにします。GetType() を override して "Audio" を返します。Play() を Implement し、"Playing audio: {Title} by {Artist}" を返します。
  • Program.cs:main file で、input values を使用して AudioFile を Create します。class の両方の側面を示すため、abstract class から Inherits した GetInfo()GetType() を呼び出し、その後 interface からの Play() を呼び出します。audio file を IPlayable variable に格納し、interface polymorphism を示すために Play() をもう一度呼び出します。

3つの input を受け取ります。

  • Song title
  • Duration in seconds
  • Artist name

次の形式で output を Print します。

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

たとえば、input が Bohemian Rhapsody354Queen の場合、output は次のようになります。

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

AudioFile が両方のアプローチの利点を得ていることに注目してください。abstract class から共通の実装(GetInfo())を Inherits すると同時に、IPlayable contract も満たしています。abstract class は関連するメディア型にコードの再利用を提供し、interface は、継承階層を共有していない任意の関連のない class でも "playable" にできるようにします。

自分で試してみよう

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#オンラインコンパイラ