[Java入門]3.8 ガソリンタンクを独立させる

これまで、ガソリンは車に入れるものと単純に考えてきました。しかし実際の車にはガソリンタンクが合って、ガソリンはそのガソリンタンクに入れておくのが普通です。

ガソリンタンクは車の内部にあって、そこにガソリンを入れるための給油口を用意したり、ガソリンの残量を示すメーターを取り付けているわけです。ガソリンがどのくらい残っているのかとか、後何リットル入るかなどの情報は、実は車自体の情報ではなく、ガソリンタンクの情報なのです。

しかし、ガソリンを入れるときには、普通「車に入れる」と考えます。実際、ガソリンスタンドに行って、「この車の内部にあるガソリンタンクを満タンにしてください」という人はまずいないでしょう。

こうして考えてみると、ガソリンタンクと言うのもまたクラスとしてとらえることができるでしょう。用量が決まっていて、今どのくらいはいっているのかを情報として持っており、満タンかどうかを調べることができて、もちろんガソリンを入れることもできる。変数やメソッドが思い浮かんでは来ませんか?

さらに、ここまでの例で車の情報や機能として考えていたものは、実は車のガソリンタンクが持っている情報や機能だったとも言えるかもしれません。

3.8.1 Tank クラス

それではここで、ガソリンタンクのクラスを定義してみましょう。今まで考えてきた車そのものと思っていいわけですから、以前の車クラスを参考にソースを書いてみましょう。変数やメソッドは同様のはずです。

クラスの名前はTankとします。なお、このソースは $chapter3/ex7/Tank.javaにあります。

Java: Tank.java
class Tank {

  private int capacity_;
  private int remain_;

  public Tank(int tankCapacity, int initialRemain) {
    capacity_ = tankCapacity;
    remain_ = initialRemain;
  }

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

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

}

変数が2つとコンストラクタがひとつ、そしてメソッドが2つ用意されています。タンクといっても、ものによって容量が異なるので、変数capacity_からstaticの指定を外してインスタンスごとに保持するようにしています。そして、コンストラクタでタンク全体の容量とガソリンの残量を与えるようにしました。

C++: Tank.h
#ifndef TANK_H__
#define TANK_H__

class Tank {

private:
  int capacity_;
  int remain_;

public:
  Tank(int tankCapacity, int initialRemain);
  bool isNotFull() const;
  void refuel(int amount);

};

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

Tank::Tank(int tankCapacity, int initialRemain) {
  capacity_ = tankCapacity;
  remain_ = initialRemain;
}

bool Tank::isNotFull() const {
  return remain_ < capacity_;
}

void Tank::refuel(int amount) {
  remain_ = remain_ + amount;
}
C#: Tank.cs
class Tank {

  private int capacity_;
  private int remain_;

  public Tank(int tankCapacity, int initialRemain) {
    capacity_ = tankCapacity;
    remain_ = initialRemain;
  }

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

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

}
VB: Tank.vb
Class Tank

    Private capacity_ As Integer
    Private remain_ As Integer

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

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

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

  End Class

3.8.2 Car クラスへの修正

タンクのクラスができたので、車クラスがこれを使うように変更しましょう。

Carクラスはガソリンの残量を直接持つかわりに、Tankクラスのインスタンスを保持するように変更します。変数の名前は、わかりやすくtank_ということにしましょう。ガソリンタンクの残量はタンク自身が保持するようになったので、Carクラスには変数remain_は必要ありません。定義を省いてしまいましょう。

変数tankCapacity_はどうでしょうか。車はあいかわらずタンクの容量が一定ということにして、これの定義は残すことにしましょうか。

もちろんメソッドとコンストラクタも変更する必要があります。それぞれを変数tank_を使うように書き換えると、Carクラスのソースは次のようになります。このソースは$chapter3/ex7/Car.javaにあります。

Java: Car.java
class Car {

  private static final int tankCapacity_ = 50;
  private Tank tank_;

  public Car(int initialRemain) {
    tank_ = new Tank(tankCapacity_, initialRemain);
  }

  public boolean isNotFull() {
    return tank_.isNotFull();
  }

  public void refuel(int amount) {
    tank_.refuel(amount);
  }

  public static int tankCapacity() {
    return tankCapacity_;
  }

}

コンストラクタでは、タンクの容量とガソリン残量を与えてTankクラスのインスタンスを生成し、変数tank_に保持しています。2つのメソッドでは、自分で残量と容量を比較したり、残量に給油分を足したりしないで、tank_のメソッドを呼び出していることに注目してください。このようにしてオブジェクトの内部で、別なオブジェクトの機能を使うことができるのです。

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

#include "Tank.h"

class Car {

private:
  static const int tankCapacity_;
  Tank tank_;

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

};

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

const int Car::tankCapacity_ = 50;

Car::Car(int initialRemain) : tank_(tankCapacity_,initialRemain) {
}

bool Car::isNotFull() const {
  return tank_.isNotFull();
}

void Car::refuel(int amount) {
  tank_.refuel(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 tank_ As Tank

  Public Sub New(ByVal initialRemain As Integer)
    tank_ = New Tank(tankCapacity_, initialRemain)
  End Sub

  Public Function isNotFull() As Boolean
    Return tank_.isNotFull()
  End Function

  Public Sub refuel(ByVal amount As Integer)
    tank_.refuel(amount)
  End Sub

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

End Class

3.8.3 実行結果

車クラスの使い方自体は変わっていませんので、その他のクラスについては特に変更する必要はありません。したがって、実行結果も毎回の給油量が変化するだけで、基本的には同じです。

  For all gas stations:
    initial amount? 250
    unit price? 108

  Select command:
    1) Normal 2) Discount other)exit 1

  Car no. 0       1       2       3       4
  amount: 14      29      4       22      32
  price:  1512    3132    432     2376    3456
  total: 101 [liter] / 10908 [yen]

  Select command:
    1) Normal 2) Discount other)exit 2
  borderline amount? 25
  Car no. 0       1       2       3       4
  amount: 28      2       31      30      6
  price:  2884    216     3193    3090    648
  total: 97 [liter] / 10031 [yen]

  Select command:
    1) Normal 2) Discount other)exit 3

ところで賢明な読者の方々は、ガソリンスタンドにも実はタンクがあるということに気づかれていることでしょう。まさしくその通りです。ガソリンスタンドの定義も、タンククラスを利用するように書き換えることができます。これは読者への課題ということにしますので、是非チャレンジしてみてください。

prev next

この記事の投稿者: επιστημη