今回のテーマ
こんにちは、サルモリです。今回はオブジェクト指向のポリモーフィズムについて解説します。
今回の記事はサルモリと一緒に学ぶ形にしていきたいと思います。オブジェクト指向のシリーズはサルモリと学んでいく感じにしますかね。
では、やっていきましょう!!
サルモリはロボットクラスを作りました。
サルモリは早速いつも良く使用しているロボットクラスに攻撃メソッドを追加しました。
そのクラスが下記のクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Robot { string name, maker, color, weapon; public Robot (string _name,string _maker,string _color,string _weapon) { name = _name; maker = _maker; color = _color; weapon = _weapon; } public string getName() { return name; } public string getMaker() { return maker; } public string getColor() { return color; } //下記のメソッドを追加しました。 public void Attack() { Console.WriteLine(weapon); } } |
Attackメソッドを追加しています。
下記のソースコードで正しく動作するか確認してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using System; using System.Collections.Generic; class Program { static void Main(string[] args) { List<Robot> robotlist = new List<Robot>(); robotlist.Add(new Robot("Rockman", "Dr_Light", "Blue", "チャージショット")); robotlist.Add(new Robot("Fireman", "Dr_Wily", "Red", "ファイヤーストーム")); robotlist.Add(new Robot("Iceman", "Dr_Wily", "Blue", "アイススラッシャー")); foreach (var robot in robotlist) { Console.WriteLine(robot.getName()); robot.Attack(); } } } |
出力結果
Rockman
チャージショット
Fireman
ファイヤーストーム
Iceman
アイススラッシャー
ロボットの攻撃を別の処理にしたい
正しく動きましたが、一つ問題が発生します。
サルモリはファイヤーマンの攻撃の場合は出力を2つにアイスマンの場合は出力を4つに分けたいと考えました。
各ロボットの処理はこのような感じにしたいです。
ロックマンの攻撃
1 |
Console.WriteLine("チャージショット"); |
ファイヤーマンの攻撃
1 2 |
Console.WriteLine("ファイヤー"); Console.WriteLine("ストーム"); |
アイスマンの攻撃
1 2 3 4 |
Console.WriteLine("ア"); Console.WriteLine("イ"); Console.WriteLine("ス"); Console.WriteLine("クラッシャー"); |
上記のような処理にするには、ロボットクラスだけでは出来ません。
そこで使用するのが抽象クラスです!!
抽象クラスの実装
早速、抽象クラスを作ってみましょう。攻撃のみ抽象メソッドにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//ロボットクラスを抽象クラスにしました。 abstract class Robot { string name, maker, color; public Robot (string _name,string _maker,string _color) { name = _name; maker = _maker; color = _color; } public string getName() { return name; } public string getMaker() { return maker; } public string getColor() { return color; } //下記のメソッドを抽象メソッドにしました。 public abstract void Attack(); } |
抽象クラスや抽象メソッドにするには、「abstract」を付け加えてあげましょう。
続けて、この作った抽象クラスを継承したクラスを作ります。
抽象クラスの継承
ロボット抽象クラスを継承したロックマンクラス、ファイヤーマンクラス、アイスマンクラスを作りました。
下記にソースコードを書きます。抽象メソッドとなっているAttackメソッドのみそれぞれの処理を記述しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class Rockman : Robot { public Rockman(string _name, string _maker, string _color) : base(_name, _maker, _color) { } public override void Attack() { Console.WriteLine("チャージショット"); } } class Fireman : Robot { public Fireman(string _name, string _maker, string _color) : base(_name, _maker, _color) { } public override void Attack() { Console.WriteLine("ファイヤー"); Console.WriteLine("ストーム"); } } class Iceman : Robot { public Iceman(string _name, string _maker, string _color) : base(_name, _maker, _color) { } public override void Attack() { Console.WriteLine("ア"); Console.WriteLine("イ"); Console.WriteLine("ス"); Console.WriteLine("クラッシャー"); } } |
このソースコードのポイントは下記の4つです。
クラスの継承方法は「クラス : 親クラス」で可能です。
コンストラクタのbaseで親クラスのコンストラクタの処理を行っています。
継承した子クラスは親クラスの引数を使用することが出来ます。
親クラスにある抽象メソッドは必ず子クラスで実装しなければなりません。
サルモリは早速下記のコードで正しく動作するか確認してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using System; using System.Collections.Generic; class Program { static void Main(string[] args) { List<Robot> robotlist = new List<Robot>(); robotlist.Add(new Rockman("Rockman", "Dr_Light", "Blue")); robotlist.Add(new Fireman("Fireman", "Dr_Wily", "Red")); robotlist.Add(new Iceman("Iceman", "Dr_Wily", "Blue")); foreach (var robot in robotlist) { Console.WriteLine(robot.getName()); robot.Attack(); } } } |
command
Rockman
チャージショット
Fireman
ファイヤー
ストーム
Iceman
ア
イ
ス
クラッシャー
今回のように実装することで何がうれしいか?
今回のように各ロボットクラスを作り、実装すると呼び出し元のプログラムを変更せずに実装できます。
抽象クラス使用して各ロボットクラスを作らずにロボットクラスのみの場合は下記のようなコードになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
using System; using System.Collections.Generic; class Program { static void Main(string[] args) { List<Robot> robotlist = new List<Robot>(); robotlist.Add(new Robot("Rockman", "Dr_Light", "Blue")); robotlist.Add(new Robot("Fireman", "Dr_Wily", "Red")); robotlist.Add(new Robot("Iceman", "Dr_Wily", "Blue")); foreach (var robot in robotlist) { Console.WriteLine(robot.getName()); if (robot.getName() == "Rockman") { robot.Attack("チャージショット"); } else if (robot.getName() == "Fireman") { robot.Attack("ファイヤーストーム"); robot.Attack("ストーム"); } else { robot.Attack("ア"); robot.Attack("イ"); robot.Attack("ス"); robot.Attack("クラッシャー"); } } } } class Robot { string name, maker, color; public Robot (string _name,string _maker,string _color) { name = _name; maker = _maker; color = _color; } public string getName() { return name; } public string getMaker() { return maker; } public string getColor() { return color; } //下記のメソッドを追加しました。 public void Attack(string weapon) { Console.WriteLine(weapon); } } |
この処理に対し、例えばクイックマンを追加したい場合、Mainメソッドにif文の条件をさらに追加しなければなりません。
今回の少ない処理のみであれば、いいのですが大きなシステムになるほど段々と収集がつかなくなっていきます。
ソースコードを書く人が全く別の人だとしたら、If文の内容が永久的に増えていきます。恐ろしいですね。
そこで初めに紹介したオブジェクト指向やポリモーフィズムを使用したソースコードにしておけば、
これから何回も修正やロボットが追加となった場合も分かりやすくソースコードの修正が行えるわけです。
因みにクイックマンを追加した場合下記のような処理となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
using System; using System.Collections.Generic; class Program { static void Main(string[] args) { List<Robot> robotlist = new List<Robot>(); robotlist.Add(new Rockman("Rockman", "Dr_Light", "Blue")); robotlist.Add(new Fireman("Fireman", "Dr_Wily", "Red")); robotlist.Add(new Iceman("Iceman", "Dr_Wily", "Blue")); robotlist.Add(new Quickman("Quickman", "Dr_Wily", "Red")); foreach (var robot in robotlist) { Console.WriteLine(robot.getName()); robot.Attack(); } } } abstract class Robot { string name, maker, color; public Robot (string _name,string _maker,string _color) { name = _name; maker = _maker; color = _color; } public string getName() { return name; } public string getMaker() { return maker; } public string getColor() { return color; } //下記のメソッドを追加しました。 public abstract void Attack(); } class Rockman : Robot { public Rockman(string _name, string _maker, string _color) : base(_name, _maker, _color) { } public override void Attack() { Console.WriteLine("チャージショット"); } } class Fireman : Robot { public Fireman(string _name, string _maker, string _color) : base(_name, _maker, _color) { } public override void Attack() { Console.WriteLine("ファイヤー"); Console.WriteLine("ストーム"); } } class Iceman : Robot { public Iceman(string _name, string _maker, string _color) : base(_name, _maker, _color) { } public override void Attack() { Console.WriteLine("ア"); Console.WriteLine("イ"); Console.WriteLine("ス"); Console.WriteLine("クラッシャー"); } } //クイックマンを追加 class Quickman : Robot { public Quickman(string _name, string _maker, string _color) : base(_name, _maker, _color) { } public override void Attack() { Console.WriteLine("クイックブーメラン"); } } |
出力結果
Rockman
チャージショット
Fireman
ファイヤー
ストーム
Iceman
ア
イ
ス
クラッシャー
Quickman
クイックブーメラン
クイックマンを簡単に追加することができました。
このように実装しておけば、ロボットの追加だけでなく、削除や名前の変更なども簡単に行えます。
今回使用した技術用語についてまとめ
今回使用したオブジェクト指向の用語をおさらいしていきます。
ポリモーフィズム:今回実装したソースコードでは、Mainメソッドで呼び出したAttackメソッドの部分を表します。Mainメソッドでは、Attackメソッドを呼びだしているだけだが、各ロボットで動作が切り替わっています。このことをポリモーフィズムと言います。
抽象クラス:abstractを指定したクラスです。抽象メソッドであえて処理を書かずに子クラスに具体的な処理を書かせることでポリモーフィズムなどを実装することが可能となります。
継承クラス:親クラスで継承できることを覚えよう!
今回の記事は長くなりましたが、オブジェクト指向のポリモーフィズムについて解説しました。
正直に言いますと、今回の例ならC#のラムダ式を使用すればMainクラスにif文を追加しなくても実装することは可能です。
C#をやるならラムダ式を使う方法も選択肢として覚えておいた方が良いと思うので別に記事を書いていきたいと思います。
今回は長くなりましたが、最後まで見て頂き有難うございました。
何回も読み返せば、ポリモーフィズムについて学べると思うので何回もチャレンジしてください。