[第14回] 抽象クラス(abstract)の使い方を学ぶ|Unityで学ぶC#入門


「Unityで学ぶC#入門」の連載第13回は「リストの基本的な使い方を学ぶ」でした。

第14回の今回は抽象クラスの使い方を学びます。

抽象クラスや抽象メソッドと呼ばれるものはオブジェクト指向プログラミングには欠かせない考え方の一つです。

抽象クラスは通常のクラスと違って制約の多いクラスで使う意味がわからないという方が多くいらっしゃいます。

そこで今回は抽象クラスについて何ができて、いったいどのようなメリットが存在するのかをわかりやすく解説していきます。

クラスの概念を理解していないと抽象クラスを理解するのに苦労してしまうかもしれませんので、もし心配な方は以下記事をご参考ください。

参考記事)クラスの基本的な使い方を学ぶ

抽象クラスについて

抽象クラス(abstractクラス)とは「抽象メソッドを1つ以上持つクラス」のことです。

抽象メソッドとは中身の処理が記述されていないメソッドのことで、基本的に以下の情報を持っています。

  • メソッド名
  • 戻り値の型
  • 引数の型
  • 引数名

これだけではよくわからないので抽象クラスについてのイメージ図をご覧ください。

抽象クラス概念

抽象クラスは図のようにサブクラスと呼ばれるクラスに継承され利用されます。つまり、サブクラスが利用しない限りこのクラスを利用することはできません(直接インスタンス化できません)。

継承とは、オブジェクト指向プログラミングにおいてよく利用される言葉ですのであえて使用しますが特別難しい意味はありません。ここでは単純に「抽象クラスを利用すること」という意味で使用しています。

サブクラスとは抽象クラスXのような親クラスを持つクラスのことです。また、サブクラスは複数の親クラスを持つことができません。

図ではクラスXがA~Cのクラスに継承されています。継承先のクラスでは抽象クラスで宣言された抽象メソッドを具体化(オーバーライド)する必要があります。また、サブクラスでは抽象クラスのもつメソッド以外のメソッドを定義することも可能です。

以上のことから、この抽象クラスには次のような特徴があることがわかります。

  • 直接インスタンス化できない(単体利用不可)
  • 抽象クラスのメソッドをサブクラスで必ずオーバーライドする
  • 複数の親クラスを持つことができない

抽象クラスを利用するメリット

抽象クラスが利用される大きなメリットの一つとして実装の仕方を決めることができる点にあります。

抽象クラスに記述されている抽象メソッドを必ずオーバーライドする必要があるので実装忘れが減ります。つまり、抽象クラスに実装してほしいメソッドを全て準備しておくことで抽象メソッドを作成した人以外が実装する場合にも必ずそのメソッドがサブクラスで実装されるということです。これにより実装の仕方を決定することができます

それだけでなく、クラス間の関係がわかりやすくなるのでプログラム自体がスッキリします。

抽象クラスの使い方

次にC#での抽象クラスの使用方法をご紹介します。

まずは抽象クラスの記述方法です。

classの前に「abstract」をつけることでそのクラスを抽象クラスにすることができます。

抽象メソッドを宣言する場合は、戻り値の型の前に「abstract」をつけます。

次に作成した抽象クラスを利用する方法です。

クラス定義の後ろに「: 継承したいクラス」をつけることで指定したクラスを継承することができます。

また、オーバーライドするメソッドには「override」をつける必要があります。

以上が抽象クラスの実装方法です。ここから具体例をみていきましょう。

RPGで抽象クラスを考える

具体例として簡易的なRPGゲームを考えてみましょう。

今回はプレイヤー側のみを考えます。

RPGプレイヤー

RPGのプレイヤー側は4人のキャラクターで構成されています。

それぞれ職業を持っていて、攻撃方法も異なります。このキャラクター達を作成するときに抽象クラスを利用した場合とそうでない場合を考えていきます。

まずは抽象クラスを利用しない場合を考えます。

抽象クラスを使用しない場合

こちらはコードに記述した方がわかりやすいので実際にコードにしてみましょう。

ここでは勇者クラスである「Brave.cs」を確認してみましょう。

コンストラクタで名前を設定でき、攻撃メソッドによって攻撃できます。(ログ出力のみ)

次にこのPersonを生成するクラス「GameController.cs」を確認してみましょう。

勇者をインスタンス化して各メソッドを呼び出しています。

同じような考え方で「Mage」「Monk」「Warrior」クラスを作成することでそのほかの職業も作成します。

その際、抽象クラスを利用していない場合に起こりうることとして、以下のようなことが挙げられます。

  • どのキャラクターにも存在しなければならないHPやMP、攻撃メソッドを実装し忘れる
  • 攻撃メソッドの命名にブレが生じる(「Attack()」や「attack()」)
  • 同じキャラクターであるのに実装方法が大きく異なる

上記のような問題を抽象クラスを用いることによって回避することができます。

抽象クラスを使用する場合

抽象クラスを利用する場合、共通化できる部分を洗い出して一つにまとめます。

今回はCharacterクラスを作成してそれぞれの職業クラスに派生させていきます。(下記イメージ参考)

キャラクターの親子関係

まずはどのキャラクターにも適用できる抽象クラス「Character」を作成します。

抽象クラスであるCharacterにはキャラクターが持つべきHPなどのフィールドと、抽象メソッドであるAttackメソッドがあります。

また、各情報にアクセスするためのGetterとプロフィールを出力するメソッドも実装してあります。

HPやMPに関してはSwitch文で値を設定できるように実装してあります。

参考記事)[第15回] 列挙型(Enum)の使い方を学ぶ

それではこのCharacterクラスを親にもつクラス「Brave」を確認してみましょう。

Braveでは抽象メソッドの処理を記述します。また、コンストラクタでは親クラスのフィールドに値を設定するため、「base()」を利用して親クラスのコンストラクタを利用します。

コントローラークラスは先ほどと同じように利用することができます。

以下に勇者以外の実装方法も記述しておきます。

↓ Mage.cs

↓ Monk.cs

Monkクラスのように、抽象クラスで宣言したメソッド以外のメソッドも実装可能です。

↓ Warrior.cs

以上のように実装することで各クラスがシンプルになります。

もしAttackメソッドをオーバーライドし忘れてしまっても、以下のようなエラーが発生するので記述忘れの心配もいりません。

オーバーライドし忘れエラー

まとめ

いかがでしたでしょうか。

今回は抽象クラスの使い方を学びました。

抽象クラスを利用することでクラスをわかりやすくまとめたり、実装ミスを減らしたりすることができます。

また、外部ライブラリなどを利用する際にも抽象クラスを介して利用するものも存在しますので、使用方法を理解しておいて損はありません。

今回さりげなく使用してしまったEnumについても次回以降の連載で解説する予定ですので乞うご期待ください。

他の回の記事について気になる方はこちらをクリック

 

そのほかの参考記事)abstract (C# リファレンス) (Microsoft公式)

列挙型enumについて)[第15回] 列挙型(Enum)の使い方を学ぶ


キャラクターの親子関係

この記事はいかがでしたか?よければシェアをお願いします。

Furui