抽象クラスとポリモーフィズム

 抽象クラスを使用してポリモーフィズムを実現させてみましょう。スーパークラスの型でサブクラスのオブジェクトを扱えることを思い出してください。抽象クラスの型で、抽象クラスを実装したオブジェクトを格納することができます。ただしその場合、呼び出すことができるのは抽象クラスで定義しているメンバだけです。

 これからいろいろな乗り物を操作できるシミュレーターを作成してみましょう。乗り物クラスを継承したスポーツカークラス、タクシークラスを作成して利用してみましょう。ポリモーフィズムを確認するために、乗り物のテストをするテストドライバークラスを作成して、テストドライバーが乗り物を動かすようにします。

○ プロジェクト

 プロジェクトを作成して確認してみましょう。

プロジェクトの種類 コンソール アプリケーション
プロジェクト名 AbstractPlymoTest

サンプルダウンロード

○ 作成の準備

 「AbstructClassTest」プロジェクトを修正して作成しましょう。AbstructClassTestフォルダーをコピーして、作成するプロジェクト名にフォルダー名を変更してください。

○ プログラム

 次に、乗り物クラスを継承したタクシークラスを作成しましょう。

Taxi.cs

  1. class Taxi
  2.  : Vehicle
  3. {
  4.  //フィールド==========
  5.  private int maxspeed;  //最大スピード
  6.  //プロパティ==========
  7.  public int Unchin { get; set; }  //売上金額
  8.  public double Distance { get; set; }  //走行距離
  9.  public Boolean Aboard { get; private set; } //乗車状態
  10.  //コンストラクタ==========
  11.  public Taxi(double gas, int maxspeed)
  12.   : base(gas)
  13.  {
  14.   //ガソリン量が要領を超えている場合は、最大容量をガソリン残量にセット
  15.   if (this.Gas > 70)
  16.   {
  17.    this.Gas = 70;
  18.   }
  19.   this.maxspeed = maxspeed;
  20.  }
  21.  //メソッド==========
  22.  public override void SpeedUp(int sp)
  23.  {
  24.   //消費するガソリンがない場合は加速しない
  25.   if (!((this.Gas - sp / 20.0) <= 0))
  26.   {
  27.    //加速
  28.    this.Speed += sp;
  29.    this.Gas -= sp / 20.0;
  30.    //スピードが最大スピードを超えていたら、最大スピードにセット
  31.    if (this.Speed > this.maxspeed)
  32.    {
  33.     this.Speed = this.maxspeed;
  34.    }
  35.   }
  36.  }
  37.  public string RideToTaxi(int price, double distance)
  38.  {
  39.   //乗車中かどうか
  40.   if (this.Aboard)
  41.   {
  42.    return "乗車中です";
  43.   }
  44.   //走行中かどうか
  45.   if (this.Speed != 0)
  46.   {
  47.    return "停車してください";
  48.   }
  49.   //運賃、距離を加算
  50.   this.Unchin += price;
  51.   this.Distance += distance;
  52.   this.Aboard = true;
  53.   return null;
  54.  }
  55.  public string GetOffTaxi()
  56.  {
  57.   //乗車中かどうか
  58.   if (!this.Aboard)
  59.   {
  60.    return "乗車していません。";
  61.   }
  62.   //走行中かどうか
  63.   if (this.Speed != 0)
  64.   {
  65.    return "停車してください";
  66.   }
  67.   //乗車の状態を変更
  68.   this.Aboard = false;
  69.   return null;
  70.  }
  71. }

○ クラス図

○ 解説

 13行目では最大スピードを格納するフィールドを定義しています。インスタンス生成時に最大スピードを初期化し、加速するときにこのフィールドを参照して加速するかを決定します。

 16~18行目では売上金、走行距離、乗車状態のプロパティを定義しています。

 21~30行目ではコンストラクタを定義しています。このコンストラクタはガソリン量を受けとって、スーパークラスのコンストラクタを呼び出し、ガソリン量をセットしています。そのあと、ガソリンタンク容量と比較して、セットしたガソリン量が多ければ最大容量にセットするという処理をします。

 33~48行目ではSpeedUpメソッドをオーバーライドしています。抽象クラスを継承すると、抽象メソッドは必ずオーバーライドしなければならないという義務が発生します。今回は加速するときに、消費するガソリン量がない場合は加速しない、最大スピードに達していた場合はそれよりスピードが出ないようにしています。 スポーツカークラスと違い、消費する燃料を抑えています。

 50~69行目では乗車する処理を定義しています。車が走っているか、既に乗車している場合には人は乗せられないようにしています。止まっているか、空車の場合は引数として受け取った料金、距離、乗車状況を、プロパティを使って設定しています。

 71~88行目では、降車する処理を定義しています。車が走っている場合は人を降ろせないようにしています。止まっていれば降車処理をしますが、今回は乗車状態を変更しています。

○ プログラム

 次に、乗り物を運転するテストドライバークラスを作成しましょう。次にようにクラスを作成してください。

TestDriver.cs

  1. class TestDriver
  2. {
  3.  //フィールド==========
  4.  private int accelerate; //加速度合(km)
  5.  private Vehicle car;  //車
  6.  //コンストラクタ==========
  7.  public TestDriver(Vehicle car, int sp)
  8.  {
  9.   this.car = car;
  10.   this.accelerate = sp;
  11.  }
  12.  //メソッド==========
  13.  public void SpeedUp()
  14.  {
  15.   this.car.SpeedUp(this.accelerate);
  16.  }
  17.  public void SpeedDown()
  18.  {
  19.   this.car.SpeedDown(this.accelerate);
  20.  }
  21. }

○ クラス図

○ 解説

 12行目では加速度合いを保存する変数を宣言しています。また、13行目ではテストドライバーが操作する乗り物オブジェクトを保持する変数を宣言しています。この変数はVehicle型なので、Vehcileを継承したクラスであれば保持できることになります。そのためテストドライバーは乗り物を継承した様々なタイプの乗り物を動かすことができます。今回作成したタクシークラスと、先回作成したスポーツカークラスはVehicleクラスを継承しているため、保持することができます。

 16~20行目ではコンストラクタが定義されています。引数に乗り物、加速度合いを受け取り、それぞれのフィールドにセットします。テストドライバーがどの乗り物を運転するかがテストドライバーオブジェクトを作るときに決まることになります。

 23~26行目では加速処理が定義されています。ここのSpeedUpメソッドは、乗り物クラス・スポーツカークラス・タクシークラスとは直接関係はありません。そのため、別のメソッド名で定義しても問題ありません。自分が乗っている(フィールドに保持している)乗り物のSpeedUpメソッドを呼び出す記述が25行目にあります。フィールド変数にセットされているスピード分だけ加速します。

 28~31行目では減速処理が定義されています。ここのSpeedDownメソッドは、乗り物クラス・スポーツカークラス・タクシークラスとは直接関係はありません。そのため、別のメソッド名で定義しても問題ありません。ここでの振る舞いは、前述したSpeedUpメソッドと同じです。自分が乗っている(フィールドに保持している)乗り物を減速させます。

○ プログラム

 では次にMainメソッドを修正しましょう。

Program.cs

  1. class Program
  2. {
  3.  //車の状態を表示するメソッド
  4.  public static void ShowMater(Vehicle car)
  5.  {
  6.    //コンソールの表示を消す
  7.    Console.Clear();
  8.    //各プロパティの内容を表示する
  9.    Console.WriteLine("スピード:{0}km/h", car.Speed);
  10.    Console.WriteLine("ガソリン:{0}L", car.Gas);
  11.  }
  12.  static void Main(string[] args)
  13.  {
  14.  //選択メニューを表示
  15.  Console.WriteLine("走らせる車の種類を選んでください。");
  16.  Console.Write("1)スポーツカー 2)タクシー:");
  17.  string inputdata = Console.ReadLine();
  18.  //選択した車オブジェクトを生成
  19.  Vehicle car;
  20.  TestDriver driver;
  21.  if (inputdata == "1")
  22.  {
  23.   //車とテストドライバーを生成
  24.   car = new SportsCar(60);
  25.   driver = new TestDriver(car, 20);
  26.  }
  27.  else if (inputdata == "2")
  28.  {
  29.   //車とテストドライバーを生成
  30.   car = new Taxi(50, 60);
  31.   driver = new TestDriver(car, 5);
  32.  }
  33.  else
  34.  {
  35.   Console.WriteLine("車を作れませんでした。");
  36.   Console.ReadLine();
  37.   return;
  38.  }
  39.   //状態を表示
  40.   ShowMater(car);
  41.   while (true)
  42.   {
  43.    //操作を入力
  44.    Console.Write("1)加速 2)減速 9)終了:");
  45.   inputdata = Console.ReadLine();
  46.    //操作によって分岐する
  47.    switch (inputdata)
  48.    {
  49.     case "1":
  50.      //加速する
  51.    driver.SpeedUp();
  52.      ShowMater(car);
  53.      break;
  54.     case "2":
  55.      //減速する
  56.    driver.SpeedDown();
  57.      ShowMater(car);
  58.      break;
  59.     case "9":
  60.      //プログラムを終了する
  61.      return;
  62.     default:
  63.      ShowMater(car);
  64.      break;
  65.    }
  66.   }
  67.  }
  68. }

○ 解説

 25~27行目で、乗り物の選択肢を用意しました。ユーザーはスポーツカーかタクシーを選ぶことになります。32~49行目では、選択した乗り物と、テストドライバーを生成しています。乗り物に応じてテストドバイバーに加速の仕方を指示(乗り物と加速度合い)しています。

 61~79行目では、メニューを表示してテストドライバーに加速、減速などの指示を与えています。テストドライバーは自分にセットされている乗り物に対してSpeedUpメソッドやSpeedDownメソッドを呼び出して車を走らせることになります。その状態をShowMaterメソッドで表示しています。スポーツカーかタクシーのどちらのSpeedUp・SpeedDownメソッドが呼び出されるかは、テストドライバーにどちらのオブジェクトがセットされているかによって決まります。

今回のポリモーフィズムのイメージ
今回のポリモーフィズムのイメージ

 抽象クラスを使用してポリモーフィズムを実現させることができました。各オブジェクトを作り、テストドライバーにテスト実行時に乗り物を指定して加速させたり減速させたりさせ、その乗り物の状態を確認してメーターに表示しています。TestDriverクラスで呼び出しているSpeedUpメソッドやSpeedDownメソッドは、どのオブジェクトがセットされるかによって振る舞いが変わっているのを確認できたと思います。これを多態性、多様性(ポリモーフィズム)と言います。

 抽象クラスでは同じような属性を持ったオブジェクトを作ることができます。乗り物クラスであれば、乗り物の部類に属するものを定義することができます。次に、属性が全く異なるものに対しても同じ処理を指定する方法を見てみましょう。

前へ   次へ