基礎から学ぶC#入門 90日コース | 継承・抽象化・インターフェース - Day 43:抽象クラス

C# 90日で身につけるC#
スポンサーリンク
スポンサーリンク

Day 43 のゴールと全体像

Day 43 のテーマは 抽象クラスです。 キーワードは次の3つになります。

  • abstract
  • 抽象メソッド
  • 共通仕様

継承や virtualoverride を学んできた流れの中で、 「もっと設計寄りの話」に一歩踏み込む回だと思って読んでいただけるとよいと思います。

抽象クラスとは何か

「共通仕様だけを決めて、具体的な中身は子クラスに任せるクラス」

抽象クラスは、

「この種類のクラスは、こういうメソッドを必ず持っていてほしい」

という “仕様” を表現するためのクラスです。

特徴をざっくりまとめると、

  • abstract キーワードを付けて宣言する
  • インスタンスを直接作ることはできない
  • 共通のフィールド・プロパティ・メソッドを持てる
  • 「中身は書かず、形だけ決めるメソッド(抽象メソッド)」を持てる

という感じです。

まずは普通の基底クラスとの違いをイメージする

基底クラス(通常のクラス)の例

前回までの復習として、普通の基底クラスを見てみます。

using System;

// 通常の基底クラス
class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    // 仮想メソッド:鳴く
    public virtual void Speak()
    {
        Console.WriteLine($"{Name} は何か鳴きます。");
    }
}
C#

この Animal は、

  • そのまま new Animal("名前") でインスタンスを作れる
  • Speak() の中身も書かれている

という「普通のクラス」です。

抽象クラスのイメージ

一方、抽象クラスはこうです。

Animal という種類のクラスは、必ず Speak() というメソッドを持っていてほしい。 でも、どう鳴くかは動物ごとに違うから、子クラスで決めてね。」

という “ルールだけ決めるクラス” になります。

abstract キーワードで抽象クラスを作る

抽象クラス Animal の例

using System;

// 抽象クラス:インスタンスを直接作れない「設計図的なクラス」
abstract class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    // 抽象メソッド:中身を書かず、「必ず実装しなさい」という形だけ決める
    public abstract void Speak();

    // 共通処理を持つこともできる
    public void Introduce()
    {
        Console.WriteLine($"私は {Name} です。");
    }
}
C#

ここでのポイントは、

  • abstract class Animal → 抽象クラスの宣言。直接 new できません。
  • public abstract void Speak(); → 抽象メソッド。中身(本体)がありません。
  • 抽象クラスでも、普通のメソッド(Introduce)は持てる

というところです。

抽象メソッドとは何か

「必ずオーバーライドしなければならないメソッド」

抽象メソッドは、

「このクラスを継承するなら、このメソッドは必ず実装してください」

という “強制力のある仕様” を表現するためのものです。

  • 基底クラス側では「形だけ」決める
  • 派生クラス側で必ず override して中身を書く

という使い方になります。

抽象クラスを継承して具体クラスを作る

犬クラス Dog の例

// 抽象クラス Animal を継承した具体クラス:犬
class Dog : Animal
{
    public Dog(string name) : base(name)
    {
    }

    // 抽象メソッド Speak を必ずオーバーライドしなければならない
    public override void Speak()
    {
        Console.WriteLine($"{Name} は『ワン!』と鳴きます。");
    }
}
C#

猫クラス Cat の例

// 抽象クラス Animal を継承した具体クラス:猫
class Cat : Animal
{
    public Cat(string name) : base(name)
    {
    }

    public override void Speak()
    {
        Console.WriteLine($"{Name} は『ニャー!』と鳴きます。");
    }
}
C#

ここでの重要ポイントは、

  • 抽象メソッド Speak を持つ抽象クラス Animal を継承したら、 派生クラス側で必ず override しなければならない
  • もし Speak を実装しないと、コンパイルエラーになります

つまり、抽象クラスは、

「この種類のクラスは、必ずこのメソッドを持っているべきだ」

という 共通仕様をコードで表現するための仕組みなのです。

抽象クラスを使う側のコード

多態性+抽象クラスの組み合わせ

抽象クラスは、 前回の多態性(ポリモーフィズム)と組み合わせると、 とても強力になります。

class Program
{
    static void Main(string[] args)
    {
        // 抽象クラス Animal 型の変数に、具体クラス Dog と Cat を入れる
        Animal a1 = new Dog("ポチ");
        Animal a2 = new Cat("タマ");

        // 共通処理(Introduce)は抽象クラス側に書いてある
        a1.Introduce();
        a2.Introduce();

        // 抽象メソッド Speak は、各クラスごとに違う動きをする
        a1.Speak(); // ポチ は『ワン!』
        a2.Speak(); // タマ は『ニャー!』

        Console.ReadLine();
    }
}
C#

ここでの流れはこうです。

  • Animal は「動物という概念」を表す抽象クラス
  • DogCat は「具体的な動物」を表すクラス
  • Introduce は共通処理として抽象クラスに書いてある
  • Speak は「動物なら必ず持つべきメソッド」として抽象メソッドで宣言され、 各クラスが自分流に実装している

重要ポイント:

  • 抽象クラスは「共通仕様+共通処理」をまとめる場所
  • 具体クラスは「仕様を守りつつ、自分固有の振る舞いを実装する場所」

という役割分担になります。

抽象クラスがうれしい場面

1. 「必ずこのメソッドを持っていてほしい」を保証したいとき

例えば、 「すべての画面は Render() メソッドを持っていてほしい」 というルールを作りたいとします。

// 抽象クラス:画面の共通仕様
abstract class Screen
{
    public string Title { get; set; }

    public Screen(string title)
    {
        Title = title;
    }

    // すべての画面は Render を必ず実装しなければならない
    public abstract void Render();

    // 共通処理:タイトルを表示する
    public void ShowTitle()
    {
        Console.WriteLine($"=== {Title} ===");
    }
}
C#

これを継承して、 メニュー画面や設定画面を作ります。

// メニュー画面
class MenuScreen : Screen
{
    public MenuScreen() : base("メニュー")
    {
    }

    public override void Render()
    {
        ShowTitle();
        Console.WriteLine("1: 開始");
        Console.WriteLine("2: 設定");
        Console.WriteLine("0: 終了");
    }
}

// 設定画面
class SettingsScreen : Screen
{
    public SettingsScreen() : base("設定")
    {
    }

    public override void Render()
    {
        ShowTitle();
        Console.WriteLine("音量: 50");
        Console.WriteLine("明るさ: 70");
    }
}
C#

こうしておくと、

  • Screen を継承したクラスは、必ず Render() を持つ
  • 「Render を書き忘れた画面」が存在しないことを、コンパイラが保証してくれる

という状態になります。

2. 「共通仕様+共通処理」をセットで提供したいとき

抽象クラスは、

  • 抽象メソッドで「仕様」を決め
  • 通常メソッドで「共通処理」を提供する

ということができるので、

「この種類のクラスは、こういうメソッドを必ず持っていて、 こういう共通処理も使えるようになっていてほしい」

という 設計の意図をコードで表現するのに向いています。

抽象クラスの基本テンプレート

最後に、抽象クラスの基本形をテンプレートとしてまとめます。

抽象クラス

// 抽象クラス:共通仕様と共通処理をまとめる
abstract class BaseTask
{
    public string Name { get; set; }

    public BaseTask(string name)
    {
        Name = name;
    }

    // 抽象メソッド:すべてのタスクは Execute を必ず実装する
    public abstract void Execute();

    // 共通処理:タスク開始メッセージ
    public void Start()
    {
        Console.WriteLine($"タスク「{Name}」を開始します。");
    }

    // 共通処理:タスク終了メッセージ
    public void Finish()
    {
        Console.WriteLine($"タスク「{Name}」が完了しました。");
    }
}
C#

具体タスクA

class DownloadTask : BaseTask
{
    public DownloadTask(string name) : base(name)
    {
    }

    public override void Execute()
    {
        Start();
        Console.WriteLine("ファイルをダウンロードしています...");
        Finish();
    }
}
C#

具体タスクB

class UploadTask : BaseTask
{
    public UploadTask(string name) : base(name)
    {
    }

    public override void Execute()
    {
        Start();
        Console.WriteLine("ファイルをアップロードしています...");
        Finish();
    }
}
C#

使う側

class Program
{
    static void Main(string[] args)
    {
        BaseTask t1 = new DownloadTask("レポートのダウンロード");
        BaseTask t2 = new UploadTask("画像のアップロード");

        t1.Execute();
        t2.Execute();

        Console.ReadLine();
    }
}
C#

このテンプレートをベースに、

  • 抽象クラスで「共通仕様+共通処理」を決める
  • 具体クラスで「中身」を実装する

という形で、自分なりの抽象クラスを作ってみると、 abstract の感覚がぐっと身近になります。

Day 43 のまとめ

Day 43 では、

  • abstract クラス → インスタンスを直接作れない「設計図的なクラス」
  • 抽象メソッド → 中身を書かず、「必ず実装しなさい」という形だけ決めるメソッド
  • 共通仕様 → 「この種類のクラスは、こういうメソッドを必ず持っているべきだ」というルールをコードで表現すること

を、動物や画面、タスクの例を通して学びました。

抽象クラスは、

「共通仕様をきちんと決めて、 それを守る具体クラスを増やしていく」

という、設計寄りの考え方を支える重要な仕組みです。

ぜひ、

  • 自分で抽象クラスを1つ作り
  • それを継承した具体クラスを2〜3個作って
  • 抽象メソッドと共通処理を組み合わせてみる

という小さな練習を通して、 抽象クラスを 「難しい言葉」ではなく、 設計をきれいにするための頼れる道具として、少しずつ体に馴染ませていってください。

タイトルとURLをコピーしました