株式会社エス・スリー・フォー

[Java入門]3.6 車のクラスを組み合わせよう

ここまでは、自分でガソリンの量を入力していただけでした。 しかし実際には車でガソリンスタンドへ行って、車にガソリンを入れてもらうわけです。

繰り返しもできるようになったことですし、もう少しシミュレーションらしくやってみたいですね。 そこで今度は車のクラスを作りましょう。 もちろん、クラスの名前は Car です。

3.6.1 Car クラスの定義

車は、ガソリンスタンドで給油します。 ただし、必ず満タンにすることにしましょう。 また、車のタンク容量はすべて同じということにしておいて、各車のガソリンの残量は自動的に、しかも車ごとに異なるように決めるというのはどうでしょう。 これならば、ずいぶんシミュレーションらしくなりますね。

さっそくCarクラスの変数を決めましょう。

変数 内容
tankCapacity_ ガソリンタンクの容量。すべての車で同じ。
remain_ ガソリンの残量。

また。メソッドは次のようにします。

メソッド名 内容
コンストラクタ ガソリン残量の初期値を指定する。
isNotFull まだ給油できるかどうか調べる。
refuel 指定した量だけ給油する。

このクラスはどう書けばよいのか見てみましょう。 ソースは$chapter3/ex5/Car.javaに格納されています。

Java: Car.java
class Car {

  private static final int tankCapacity_ = 50;
  private int remain_;

  public Car(int initialRemain) {
    remain_ = initialRemain;
  }

  public boolean isNotFull() {
    return remain_ < tankCapacity_;
  }

  public void refuel(int amount) {
    remain_ = remain_ + amount;
  }

  public static int tankCapacity() {
    return tankCapacity_;
  }

}

C++: Car.h
#ifndef CAR_H__
#define CAR_H__

class Car {

private:
  static const int tankCapacity_;
  int remain_;

public:
  explicit Car(int initialRemain);
  virtual bool isNotFull() const;
  virtual void refuel(int amount);
  static int tankCapacity();

};

#endif
C++: Car.cpp
#include "Car.h"

const int Car::tankCapacity_ = 50;

Car::Car(int initialRemain) : remain_(initialRemain) {
}

bool Car::isNotFull() const {
  return remain_ < tankCapacity_;
}

void Car::refuel(int amount) {
  remain_ = remain_ + amount;
}

int Car::tankCapacity() {
  return tankCapacity_;
}
C#: Car.cs
class Car {

  private static int tankCapacity_ = 50;
  private int remain_;

  public Car(int initialRemain) {
    remain_ = initialRemain;
  }

  public bool isNotFull() {
    return remain_ < tankCapacity_;
  }

  public void refuel(int amount) {
    remain_ = remain_ + amount;
  }

  public static int tankCapacity {
    get {
      return tankCapacity_;
    }
  }

}
VB: Car.vb
Class Car

  Private Shared tankCapacity_ As Integer = 50
  Private remain_ As Integer

  Public Sub New(ByVal initialRemain As Integer)
    remain_ = initialRemain
  End Sub

  Public Function isNotFull() As Boolean
    Return remain_ < tankCapacity_
  End Function

  Public Sub refuel(ByVal amount As Integer)
    remain_ = remain_ + amount
  End Sub

  Public Shared ReadOnly Property tankCapacity() As Integer
    Get
      Return tankCapacity_
    End Get
  End Property

End Class

3.6.2 GasStation クラスへの修正

Carクラスを使うことにしたので、GasStationクラスとGasStationSimulationクラスも少し手直しする必要があります。 まず、GasStationクラスですが、refuelメソッドに給油量を引数として渡すかわりに、Carクラスのインスタンスを渡して満タンにしてもらうことにしましょう。 関数の引数には、int型の変数と同じ様に、クラスのインスタンスを渡すこともできるのです。

refuelメソッドは次のようになります。

public int refuel(Car aCar) {
  int charged = 0;
  while ( aCar.isNotFull() ) {
    aCar.refuel(1);
    charged = charged + 1;
  }
  return charged;
}

車への給油は1リットルずつ行なわれます。
1リットル入れては、まだ入るかどうか問い合わせています。

GasStationクラスの修正はこれだけです。

3.6.3 GasStationSimulation クラスへの修正

次に、GasStationSimulationクラスを考えてみましょう。
ガソリンの単価やタンクの初期量の指定は変わりませんから、initメソッドはそのままです。 dealCustomerメソッドだけを書き換えましょう。 このメソッドでしなければならない事をまとめてみます。

  • ガソリンの残量を車ごとに変えて決定する。
  • 車クラスのインスタンスを作る。
  • 給油する。
  • 代金を計算して売上に計上する。
  • 以上を何台分か繰り返す。

Random クラス

まず、ガソリン残量をそれぞれの車ごとに異なるように設定するために、Randomクラスという便利なクラスを利用します。 このクラスのインスタンスを使うと、ある分布にしたがって乱数を発生させることができます。 詳しいことは抜きにして、使い方を見てみましょう。

  Random aRandom = new Random();
  int anInt = aRandom.nextInt();
  int rangedInt = anInt % randomRange;

このように、new演算子でインスタンスを生成して、nextIntメソッドを呼び出すだけで乱数が得られます。 しかしこれでは、得られる数の範囲がint全体に渡ってしまうので、必要な範囲(この場合はrandomRange)に収まるように割り算した余りを使います。 %が余りを求める演算子です。

また、このクラスを利用するためには、ソースの先頭に次のような記述を追加します。

import java.util.Random;

■ C++では乱数の生成には関数std::rand を用います。

  int anInt = std::rand();
  int rangedInt = anInt % randomRange;

std::rand は0以上RAND_MAX以下の乱数を発生します。

また、std::randを使用するためには、ソースの先頭に次のような記述を追加します。

#include <cstdlib>

■ C#,VB では、System.Random を用います。

  System.Random aRandom = new System.Random();
  int rangedInt = aRandom.Next(randomRange);

Nextメソッドは、指定した値より小さい0以上の乱数を返します。□

配列

ところで、何回分かの表示が縦に並んでしまうとちょっと見にくいので、横に並べて表示したいですね。 給油した量を覚えておいて、後から表示するというようにできないでしょうか。 また、「何番目の車の給油量はどれだけだった」と言うことさえ覚えておけば、後から売上も計上できそうです。

こういったことを可能にするのが、配列です。
配列は、同じ型の変数がたくさん必要な場合に便利です。
次のdealCustomerメソッドを例に、配列をどのように使うのか見てみることにしましょう。

  public void dealCustomer() {
    Random aRandom = new Random();
    int[] charged = new int[5];
    int totalAmount = 0;
    for ( int i = 0; aGS_.isNotEmpty() && i < charged.length; i = i + 1 ) {
      int initial = aRandom.nextInt() % Car.tankCapacity();
      if ( initial < 0 ) {
        initial = -initial;
      }
      Car aCar = new Car(initial);
      charged[i] = aGS_.refuel(aCar);
      totalAmount = totalAmount + charged[i];
    }
    System.out.print("Car no.");
    for ( int i = 0; i < charged.length; i++ ) {
      System.out.print("\t" + i);
    }
    System.out.print("\namount:");
    for ( int i = 0; i < charged.length; i++ ) {
      System.out.print("\t" + charged[i]);
    }
    System.out.print("\nprice:");
    for ( int i = 0; i < charged.length; i++ ) {
      int price = aGS_.price(charged[i]);
      aGS_.addSales(price);
      System.out.print("\t" + price);
    }
    System.out.println("\ntotal: " + totalAmount + " [liter] / "

                                   + aGS_.totalSales() + " [yen]");
  }

このソースコードを見ると、まずはじめにRandomクラスのインスタンスを作っています。 乱数の発生を5回繰り返すために、一度作ったインスタンスを変数に入れて使い回しています。 その次の行で、それぞれの車に給油した量を保持しておくための配列変数を宣言しています。

  int[] charged = new int[5];

ここでもコンストラクタと同様にnew演算子が使われていることに注意してください。型の後ろに[]を付けて、これが配列用の変数であることを表しています。この行では、int型の要素が5つある配列を作り、それをcharged変数に入れています。

その後、forが4回使われています。それぞれ、車を作って給油し給油量を覚える、車番号を横一列に表示、給油した量を横一列に表示、代金を横一列に表示ということを行なっています。

どのforにも、i < charged.length という条件があることに注意してください。この条件は、配列の何番目の要素を処理しているかを調べているのです。配列の何番目かを表す数字を添字といいいますが、添字は[i]というように大括弧でくくって変数名の後ろに付けます。

このとき、いちばん最初の要素を示す数は0ということになっています。したがって、

  for ( int i = 0; i < charged.length; i++ ) {

というforでは、iは0から始まり、charged.length - 1になるまで、charged.length回繰り返すわけです。

3.6.4 ソースと実行結果

さて、ひととおりメソッドが出そろったので、GasStationSimnulationクラスのソースをまとめておきます。

このソースは、$chapter3/ex5/GasStationSimulation.javaにあります。

Java: GasStationSimulation.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Random;

class GasStationSimulation {

  private GasStation aGS_;

  private int askInt(String message) {
    try {
      System.out.print(message);
      System.out.flush();
      String line = new BufferedReader(new InputStreamReader(System.in)).readLine();
      return Integer.parseInt(line);
    } catch ( java.io.IOException e ) {
      System.out.println("could not get input. Sorry.");
      return 0;
    }
  }

  public void init() {
    System.out.println("For the gas station:");
    int initial = askInt("  initial amount? ");
    int price = askInt("  unit price? ");
    GasStation.setUnitPrice (price);
    aGS_ = new GasStation(initial);
  }

  public void dealCustomer() {
    Random aRandom = new Random();
    int[] charged = new int[5];
    int totalAmount = 0;
    for ( int i = 0; aGS_.isNotEmpty() && i < charged.length; i = i + 1 ) {
      int initial = aRandom.nextInt() % Car.tankCapacity();
      if ( initial < 0 ) {
        initial = -initial;
      }
      Car aCar = new Car(initial);
      charged[i] = aGS_.refuel(aCar);
      totalAmount = totalAmount + charged[i];
    }
    System.out.print("Car no.");
    for ( int i = 0; i < charged.length; i++ ) {
      System.out.print("\t" + i);
    }
    System.out.print("\namount:");
    for ( int i = 0; i < charged.length; i++ ) {
      System.out.print("\t" + charged[i]);
    }
    System.out.print("\nprice:");
    for ( int i = 0; i < charged.length; i++ ) {
      int price = aGS_.price(charged[i]);
      aGS_.addSales(price);
      System.out.print("\t" + price);
    }
    System.out.println("\ntotal: " + totalAmount + " [liter] / "

                                   + aGS_.totalSales() + " [yen]");
  }

  public static void main(String[] args) {
    GasStationSimulation simulation = new GasStationSimulation();
    simulation.init();
    simulation.dealCustomer();
  }

}

C++: GasStationSimulation.cpp
#include <iostream>

#include <cstdlib> // rand
#include <vector>  // vector

#include "GasStationSimulation.h"
#include "Car.h"
#include "GasStation.h"

GasStationSimulation::~GasStationSimulation() {
  delete aGS_;
}

int GasStationSimulation::askInt(const std::string& message) {
  std::cout << message << std::flush;
  int result;
  if ( std::cin >> result ) {
    return result;
  }
  std::cout << "could not get input.  sorry." << std::endl;
  return 0;
}

void GasStationSimulation::init() {
  std::cout << "For the gas station:" << std::endl;
  int initial = askInt("  initial amount? ");
  int unitPrice = askInt("  unit price? ");
  GasStation::setUnitPrice(unitPrice);
  aGS_ = new GasStation(initial);
}

void GasStationSimulation::dealCustomer() {
  std::vector<int> charged(5);
  typedef std::vector<int>::size_type size_type;
  for ( size_type i = 0; aGS_->isNotEmpty() && i < charged.size(); ++i ) {
    int initial = std::rand() % Car::tankCapacity();
    Car* aCar = new Car(initial);
    charged[i] = aGS_->refuel(aCar);
    delete aCar;
  }
  std::cout << "Car no.";
  for ( size_type i = 0; i < charged.size(); ++i ) {
    std::cout << '\t' << static_cast<unsigned>(i);
  }
  std::cout << "\namount:";
  for ( size_type i = 0; i < charged.size(); ++i ) {
    std::cout << '\t' << charged[i];
  }
  std::cout << "\nprice:";
  for ( size_type i = 0; i < charged.size(); ++i ) {
    int price = aGS_->price(charged[i]);
    aGS_->addSales(price);
    std::cout << '\t' << price;
  }
  std::cout << "\nTotal sales: " << aGS_->totalSales() << " [yen]" << std::endl;
}
C#: GasStationSimulation.cs
class GasStationSimulation {

  private GasStation aGS_;

  private int askInt(string message) {
    System.Console.Write(message);
    System.Console.Out.Flush();
    try {
      return System.Int32.Parse(System.Console.ReadLine());
    } catch ( System.Exception ) {
      System.Console.WriteLine("could not get input.  sorry.");
      return 0;
    }
  }

  public void init() {
    System.Console.WriteLine("For the gas stations:");
    int initial = askInt("  initial amount? ");
    aGS_ = new GasStation(initial);
    GasStation.unitPrice = askInt("  unit price? ");
  }

  public void dealCustomer() {
    System.Random aRandom = new System.Random();
    int[] charged = new int[5];
    int totalAmount = 0;
    for ( int i = 0; aGS_.isNotEmpty() && i < charged.Length; ++i ) {
      int initial = aRandom.Next(Car.tankCapacity);
      Car aCar = new Car(initial);
      charged[i] = aGS_.refuel(aCar);
    }
    System.Console.Write("Car no.");
    for ( int i = 0; i < charged.Length; ++i ) {
      System.Console.Write("\t" + i);
    }
    System.Console.Write("\namount:");
    for ( int i = 0; i < charged.Length; ++i ) {
      System.Console.Write("\t" + charged[i]);
    }
    System.Console.Write("\nprice:");
    for ( int i = 0; i < charged.Length; ++i ) {
      int price = aGS_.price(charged[i]);
      aGS_.addSales(price);
      System.Console.Write("\t" + price);
    }
    System.Console.WriteLine("\ntotal: " + totalAmount + " [liter] / "

                                         + aGS_.totalSales + " [yen]");
  }

  [System.STAThread]
  static void Main(string[] args) {
    GasStationSimulation simulation = new GasStationSimulation();
    simulation.init();
    simulation.dealCustomer();
  }

 }
VB: GasStationSimulation.vb
Class GasStationSimulation

  Private aGS_ As GasStation

  Private Function askInt(ByVal message As String)
    System.Console.Write(message)
    System.Console.Out.Flush()
    Try
      Return System.Int32.Parse(System.Console.ReadLine())
    Catch e As System.Exception
      System.Console.WriteLine("could not get input.  Sorry.")
      Return 0
    End Try
  End Function

  Public Sub init()
    System.Console.WriteLine("For the customer:")
    Dim initial As Integer = askInt("  initial amount? ")
    GasStation.unitPrice = askInt("  unit price? ")
    aGS_ = New GasStation(initial)
  End Sub

  Public Sub dealCustomer()
    Dim aRandom As System.Random = New System.Random()
    Dim charged(5) As Integer
    Dim totalAmount As Integer = 0
    Dim i As Integer
    For i = 0 To charged.Length - 1
      If Not aGS_.isNotEmpty Then
        Exit For
      End If
      Dim initial As Integer = aRandom.Next(Car.tankCapacity)
      Dim aCar As Car = New Car(initial)
      charged(i) = aGS_.refuel(aCar)
      totalAmount = totalAmount + charged(i)
    Next
    System.Console.Write("Car no.")
    For i = 0 To charged.Length - 1
      System.Console.Write(vbTab + CStr(i))
    Next
    System.Console.Write("\namount:")
    For i = 0 To charged.Length - 1
      System.Console.Write(vbTab + CStr(charged(i)))
    Next
    System.Console.Write("\nprice:")
    For i = 0 To charged.Length - 1
      Dim price As Integer = aGS_.price(charged(i))
      aGS_.addSales(price)
      System.Console.Write(vbTab + CStr(price))
    Next
    System.Console.WriteLine(vbCrLf + "totoal: " + CStr(totalAmount) +
       " [liter] / " + CStr(aGS_.totalSales) + " [yen]")
  End Sub

  Public Shared Sub Main()
    Dim simulation As GasStationSimulation = New GasStationSimulation()
    simulation.init()
    simulation.dealCustomer()
  End Sub

End Class

実行結果は次のようになります。

  For the gas station:
    initial amount? 250
    unit price? 108

  Car no. 0       1       2       3       4
  amount: 6       14      24      30      44
  price:  648     1512    2592    3240    4752
  total: 118 [liter] / 12744 [yen]

prev next