抽象クラスとポリモーフィズム
抽象クラスを使用してポリモーフィズムを実現させてみましょう。抽象クラスを継承したクラスでは、必ず中小クラス内の抽象メソッドをオーバーライドしなければならないことを思い出してください。中小クラスを継承したクラスのオブジェクトは、必ず共通した名前のメソッドを持つことになり、そのメソッドを呼び出すことができます。
これからいろいろな乗り物を操作できるシミュレーターを作成してみましょう。乗り物クラスを継承したスポーツカークラス、タクシークラスを作成して利用してみましょう。ポリモーフィズムを確認するために、乗り物のテストをするテストドライバークラスを作成して、テストドライバーが乗り物を動かすようにします。
○ ファイル
乗り物クラスを継承したタクシークラスを作成しましょう。
ファイルの種類 |
PHPファイル |
ファイル名 |
Taxi.php |
サンプルダウンロード
○ プログラム
次のようにプログラムを入力して下さい。
Taxi.php
- <?php
- //クラスファイルを読み込む
- require_once 'Vehicle.php';
- class Taxi extends Vehicle
- {
- //プロパティ==========
- private $maxspeed = 0; //最大スピード
- private $unchin = 0; //売上金額
- private $distance = 0; //走行距離
- private $aboard = false; //乗車状態
- //コンストラクタ==========
- public function __construct($gas, $maxspeed){
- //Vehicleクラスのコンストラクタを呼び出す
- parent::__construct($gas);
- //ガソリン量が容量を超える場合は、最大容量をガソリン量にセットする
- if($this -> gas > 70){
- $this -> gas = 70;
- }
- $this -> maxspeed = $maxspeed;
- }
- //メソッド==========
- public function getUnchin(){
- return $this -> unchin;
- }
- public function getDistance(){
- return $this -> distance;
- }
- public function getAboard(){
- return $this -> aboard;
- }
- public function speedUp($sp){
- //消費するガソリンがない場合は加速しない
- if(!(($this -> gas - $sp / 20.0) <= 0)){
- //加速する
- $this -> speed += $sp;
- $this -> gas -= $sp / 20.0;
- //スピードが最大スピードを超えていたら、最大スピードをセットする
- if($this -> speed > $this -> maxspeed){
- $this -> speed = $this -> maxspeed;
- }
- }
- }
- public function rideToTaxi($price, $distance){
- //乗車中かどうか
- if($this -> aboard){
- return '乗車中です。';
- }
- //走行中かどうか
- if($this -> speed != 0){
- return '停車してください。';
- }
- //運賃、距離を加算
- $this -> unchin += $price;
- $this -> distance += $distance;
- $this -> aboard = true;
- return null;
- }
- public function getOffTaxi(){
- //乗車中かどうか
- if(!$this -> aboard){
- return '乗車していません。';
- }
- //走行中かどうか
- if($this -> speed != 0){
- return '停車してください。';
- }
- //乗車の状態を変更
- $this -> aboard = false;
- return null;
- }
- }
○ クラス図
○ 解説
8行目では最大スピードを格納するプロパティを定義しています。インスタンス生成時に最大スピードを初期化し、加速するときにこのプロパティを参照して加速するかを決定します。
9~11行目では売上金、走行距離、乗車状態のプロパティを定義しています
14~23行目ではコンストラクタを定義しています。このコンストラクタはガソリン量を受けとって、スーパークラスのコンストラクタを呼び出し、ガソリン量をセットしています。そのあと、ガソリンタンク容量と比較して、セットしたガソリン量が多ければ最大容量にセットするという処理をします。
38~50行目ではspeedUpメソッドをオーバーライドしています。抽象クラスを継承すると、抽象メソッドは必ずオーバーライドしなければならないという義務が発生します。今回は加速するときに、消費するガソリン量がない場合は加速しない、最大スピードに達していた場合はそれよりスピードが出ないようにしています。 スポーツカークラスと違い、消費する燃料を抑えています。
52~68行目では乗車する処理を定義しています。車が走っているか、既に乗車している場合には人は乗せられないようにしています。止まっているか、空車の場合は引数として受け取った料金、距離、乗車状況を、それぞれのプロパティに設定しています。
70~84行目では、降車する処理を定義しています。車が走っている場合は人を降ろせないようにしています。止まっていれば降車処理をしますが、今回は乗車状態を変更しています。
○ ファイル
次に、乗り物を運転するテストドライバークラスを作成しましょう。
ファイルの種類 |
PHPファイル |
ファイル名 |
TestDriver.php |
○ プログラム
次のようにクラスを作成してください。
TestDriver.php
- <?php
- class TestDriver
- {
- //フィールド==========
- private $accelerate = 0; //加速度合(km)
- private $car = null; //車
- //コンストラクタ==========
- public function __construct($car, $sp){
- $this -> car = $car;
- $this -> accelerate = $sp;
- }
- //メソッド==========
- public function speedUp(){
- $this -> car -> speedUp($this -> accelerate);
- }
- public function speedDown(){
- $this -> car -> speedDown($this -> accelerate);
- }
- }
○ クラス図
○ 解説
5行目では加速度合いを保存する変数を宣言しています。また、6行目ではテストドライバーが操作する乗り物オブジェクトを保持する変数を宣言しています。この変数はVehcileを継承したクラスのオブジェクトを保持させます。そのためテストドライバーは乗り物を継承した様々なタイプの乗り物を動かすことができます。今回作成したタクシークラスと、先回作成したスポーツカークラスはVehicleクラスを継承しているため、保持することができます。
9~12行目ではコンストラクタが定義されています。引数に乗り物、加速度合いを受け取り、それぞれのプロパティにセットします。テストドライバーがどの乗り物を運転するかがテストドライバーオブジェクトを作るときに決まることになります。
15~17行目では加速処理が定義されています。ここのspeedUpメソッドは、乗り物クラス・スポーツカークラス・タクシークラスとは直接関係はありません。そのため、別のメソッド名で定義しても問題ありません。自分が乗っている(プロパティに保持している)乗り物のspeedUpメソッドを呼び出す記述が16行目にあります。プロパティにセットされているスピード分だけ加速します。
19~21行目では減速処理が定義されています。ここのspeedDownメソッドは、乗り物クラス・スポーツカークラス・タクシークラスとは直接関係はありません。そのため、別のメソッド名で定義しても問題ありません。ここでの振る舞いは、前述したspeedUpメソッドと同じです。自分が乗っている(プロパティに保持している)乗り物を減速させます。
○ ファイル
次に、実際に動かすページを作成しましょう。
ファイルの種類 |
PHPファイル |
ファイル名 |
AbstractPolymoTest.php |
○ プログラム
次のようにプログラムを入力して下さい。
AbstractPolymoTest.php
- <?php
- //クラスファイルを読み込む
- require_once 'SportsCar.php';
- require_once 'Taxi.php';
- require_once 'TestDriver.php';
- ?>
- <!DOCTYPE html>
- <html lang="ja">
- <meta charset="utf-8">
- <head>
- <title>テストドライブ</title>
- </head>
- <body>
- <?php
- //車の状態を表示する関数
- function showData($vehicle){
- //状態を表示する
- print('スピード:' . $vehicle -> getSpeed() . 'km<br>');
- print('ガソリン:' . $vehicle -> getGas() . 'L<br>');
- }
- //スポーツカーインスタンスを生成する
- $car = new SportsCar(60);
- //ドライバーオブジェクトを生成する
- $driver = new TestDriver($car, 20);
- print('<b>テストドライバーがスポーツカーを運転します。</b><br>');
- //加速してみる
- print('加速します!<br>');
- $driver -> SpeedUp(50);
- showData($car);
- print('<hr>');
- //加速してみる
- print('加速します!<br>');
- $driver -> SpeedUp(50);
- showData($car);
- print('<hr>');
- //減速してみる
- print('減速します!<br>');
- $driver -> SpeedDown(50);
- showData($car);
- print('<hr>');
- //減速してみる
- print('減速します!<br>');
- $driver -> SpeedDown(50);
- showData($car);
- print('<hr>');
- //タクシーインスタンスを生成する
- $car = new Taxi(50, 60);
- //ドライバーオブジェクトを生成する
- $driver = new TestDriver($car, 5);
- print('<b>テストドライバーがタクシーを運転します。</b><br>');
- //加速してみる
- print('加速します!<br>');
- $driver -> SpeedUp(50);
- showData($car);
- print('<hr>');
- //加速してみる
- print('加速します!<br>');
- $driver -> SpeedUp(50);
- showData($car);
- print('<hr>');
- //減速してみる
- print('減速します!<br>');
- $driver -> SpeedDown(50);
- showData($car);
- print('<hr>');
- //減速してみる
- print('減速します!<br>');
- $driver -> SpeedDown(50);
- showData($car);
- print('<hr>');
- ?>
- </body>
- </html>
○ 解説
23行目はスポーツカーオブジェクトを、53行目はタクシーオブジェクトを生成しています。それぞれのオブジェクトを生成したのち、テストドライバーを生成しています。乗り物に応じてテストドライバーに加速の仕方を指示(乗り物と加速度合い)しています。
27~50、57~80行目では、テストドライバーに加速、減速などの指示を与えています。テストドライバーは自分にセットされている乗り物に対してspeedUpメソッドやspeedDownメソッドを呼び出して車を走らせることになります。その状態をshowDataメソッドで表示しています。スポーツカーかタクシーのどちらのspeedUp・speedDownメソッドが呼び出されるかは、テストドライバーにどちらのオブジェクトがセットされているかによって決まります。
今回のポリモーフィズムのイメージ
抽象クラスを使用してポリモーフィズムを実現させることができました。各オブジェクトを作り、テストドライバーにテスト実行時に乗り物を指定して加速させたり減速させたりさせ、その乗り物の状態を確認してメーターに表示しています。TestDriverクラスで呼び出しているspeedUpメソッドやspeedDownメソッドは、どのオブジェクトがセットされるかによって振る舞いが変わっているのを確認できたと思います。これを多態性、多様性(ポリモーフィズム)と言います。
抽象クラスでは同じような属性を持ったオブジェクトを作ることができます。乗り物クラスであれば、乗り物の部類に属するものを定義することができます。次に、属性が全く異なるものに対しても同じ処理を指定する方法を見てみましょう。
前へ 次へ