抽象クラスを勉強したい

はいどーもこんにちは元ニート現ニートの無職です。
先日はインターフェイスについて勉強したので、本日は抽象クラスについて勉強したいと思いますよ。

抽象クラスってなんやねん

抽象クラスが何か、というのは僕が解説するよりも他のサイトやら入門書やらを読んでもらった方が早いでしょう。
このサイトによれば「インスタンスを生成出来ないクラスのことで、 継承して使うことを前提としたクラスのこと」とのことです。
もう少しだけ説明を加えておくと、抽象クラスは抽象メソッドを保持しています。
抽象メソッドは実装がないので、そのままでは使うことができません。
つまり抽象メソッドを持つ抽象クラスは、不完全なメソッド(抽象メソッド)を持つためにインスタンスを生成できないのです(インスタンスを生成できたら、不完全なメソッドを利用できることになって都合が悪い)。
なので、継承先のクラスで抽象メソッドを実装しなければならない、ということです。

ただし注意点として、例えば変数の型として抽象クラスを指定することはできます。
これはちょうど、実装を持たないインターフェイスを変数の型として指定できることと同一です(インターフェイスについては前回少しだけ書きました。そのときも変数やプロパティの型としてインターフェイスを指定しています)。
ポリモーフィズムを実現しようと思った時に重要なので、これは覚えておいた方がよさげですね。
今回の記事でも変数の型として抽象クラスを指定しますよ。

サンプルとして何を作るか

前回のインターフェイスの記事はさすがに適当すぎたので、今回はもう少しきちんと何を作るかを考えました。
というわけで本日はズバリ「RPGでモンスターを倒した時に手に入るアイテムの処理」を作ろうと思います。

と言ってもRPGの処理を全部作っていては大変すぎるので、前提としては以下のようなものになります。

・プログラムが始まったとき、モンスターは倒したことにする
・何ターンで倒したかはランダムで決定
・ターン数によって得られるアイテムが変わる

具体的には「戦闘に勝利したメッセージを表示→宝箱を入手したメッセージを表示→何のアイテムを入手したのかのメッセージを表示」となります。

とりあえず実装してみる

いつものごとく、C#のコンソールアプリで実装します。
宝箱の種類は「普通」「いいやつ」「最高」の三種類を用意したいと思いますよ。

あとはMainメソッドを以下のような感じで書けばOKです。

ゲームを実行すると、「戦闘に勝利した!」というメッセージ(本当は宝箱の処理と関係ないのでここに入れたくないですが、まあサンプルということで)を表示したあと、「どの宝箱を開けたか(普通の宝箱・いい宝箱・最高の宝箱)」を表示し、その中身も表示します。

各宝箱の処理

さて各宝箱の処理を見てみましょう。
Tresure抽象クラスには

・サイコロを振るためのRollDiceメソッド
・宝箱を開けるためのOpenTresureBoxメソッド

があります。

RollDiceメソッドは単に1~6の値をランダムに返すだけのメソッドです。
OpenTresureBoxメソッドはShowGettingMessage抽象メソッドとSelectTresures抽象メソッドを使用し、表示する文字を決定します。

ここで注目してほしいのは、ShowGettingMessage抽象メソッドとSelectTresures抽象メソッドには具体的な中身が一切ないことです。
OpenTresureBoxメソッドの中で呼ばれてはいますが、単に呼ばれるタイミングが指定されているだけです。
このメソッドは子クラス以下でオーバーライドしなければなりません。

それでは子クラスを見ていきましょう。
ShowGettingMessageメソッドは無視して(面白い処理は何もしていないので)、SelectTresuresメソッドだけに注目してみてください。

まずはNormalTresureクラスです。
抽象メソッドがオーバーライドされていることがわかるかなと思います。
このクラスでは単に文字列をそのまま返しているだけ(薬草を返しているだけ)です。

次にBetterTresureクラスです。
このクラスのSelectTresuresメソッドは内部でRollDiceメソッドを用い、入手できるアイテムをランダムに選んでいます。

最後はBestTresureクラスです。
このクラスもBetterTresureクラスと同じようにRollDiceメソッドを使って入手アイテムをランダムに選んでいますが、それを3回繰り返すようにしています。

以上のクラスはTresureTestクラスで使用されています。
どの宝箱が選ばれるかはランダムです(想定としては、モンスターを倒したターンによって入手できる宝箱が変わる。ここでは単にランダムに選んでいるだけ)。
そのとき、Tresure型(抽象クラス)の変数_tresureを宣言し、具象クラスのインスタンスを生成しています。
これの何がよいかと言えば、Tresure型を継承しているクラスはOpenTresureBoxを実装していることが保証されているので、NormalTresureクラスでもBestTresureクラスでもBestTresureクラスでも同じようにOpenTresureBoxメソッドを呼ぶことができるのです。
各クラスのOpenTresureBox内の処理(具体的にはSelectTresuresメソッドの処理)は各クラスで全く違うものになっていますが、全て同じように扱うことができるのです!
これは便利ですね。

ただ実際のRPGって、抽象クラスを使って宝箱の処理を作ったりするんですかねえ。
宝箱の中身はもちろん、宝箱のゲーム上の見た目・宝箱を空けたときのモーションや音をインスタンスごとに設定できるようにして表示させるのが一般的なんじゃないかなあ……。
まあこの辺は単なるサンプルということで一つご勘弁を。

終わりに

本日は抽象クラスについて学びました。
抽象クラスについて学んだなら拡張メソッドも学びたい気がしますね。
ただ拡張メソッドについては去年にこんな記事を書いています。http://ntgame.wpblog.jp/2017/12/30/post-1334/

あとはデザインパターンの一つであるStrategyパターンやTemplate Methodパターンが抽象クラスとわりあい関連が深そうな気がしています。
こちらもそのうち勉強したいですね。

ほなそんな感じでまた。

フォローする