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