はじめに
オブジェクト指向プログラミングの入門書では,動物の鳴き声を使ったサンプルをよく見ますね。
リスト1 オブジェクト指向でよく見かけるサンプル
class cat {
function mew() {
return "ニャー";
}
function hasNekomimi() {
return TRUE;
}
}
// kitten is a cat.
class kitten extends cat {
function mew() {
return "ミー";
}
// hasNekomimiメソッドは,catクラスから継承される
}
// phpはダックタイピングなので,catクラスのかわりに渡せる
class catgirl {
function mew() {
return "にゃんっ";
}
function hasNekomimi() {
return TRUE;
}
}
これは一見わかりやすく思えますが,どう実際のプログラムにあてはめればよいか,初心者の方はわかりにくいのではないかという気もしました。そこで本稿では,現実の物をオブジェクトとして扱うことで,オブジェクト指向について別の面から見ることに挑戦してみたいと思います。
オブジェクト指向の基礎
まず,サンプルから見ていきましょう。前回の往復プログラムを,railクラスを使って書き換えてみました(リスト2)。
リスト2 railクラスを使う
#! /usr/local/bin/php
<?php
$url = "http://192.168.0.100/";
class rail {
var $number;
function rail($number = "00") {
$this->number = $number;
}
function drive($speed = 0) {
global $url;
if ($speed >= 0)
$command = substr("ABCDEF", min($speed, 5), 1);
else
$command = substr("abcdef", min(-$speed, 5), 1);
file_get_contents($url."?".$this->number."=".$command);
}
function sense() {
global $url;
return file_get_contents($url."?".$this->number."=S") + 0;
}
}
#
# |---r4---|---r3---|---r2---|---r1---|---r0---|
#
$r0 =& new rail("00");
$r1 =& new rail("01");
$r2 =& new rail("02");
$r3 =& new rail("03");
$r4 =& new rail("04");
for (;;) {
$r0->drive(2);
$r1->drive(3);
$r2->drive(3);
$r3->drive(2);
while ($r4->sense() == 0)
;
$r0->drive(0);
$r1->drive(0);
$r2->drive(0);
$r3->drive(0);
$r4->drive(-2);
$r3->drive(-3);
$r2->drive(-3);
$r1->drive(-2);
while ($r0->sense() == 0)
;
$r4->drive(0);
$r3->drive(0);
$r2->drive(0);
$r1->drive(0);
}
?>
「class rail {」のところがクラスの定義なのですが,今回は説明を割愛します。かわりに,railクラスの取扱説明書を書くとしたら,どうなるでしょうか。
- (1)あらかじめ「$r0 =& new rail("00");」としておきます。"00"はレールの番号です。
- (2)「$r0->drive(3);」とすると,レールに電圧がかかります。数字が大きいと,スピードも速くなります。ゼロだと停止で,マイナスだと逆方向になります。
- (3)「$r0->sense();」とすると,レールに車両があれば1,なければ0が返ります。
これにより,後半の制御部分ではレールの番号に依存しなくて済むようになりました。たとえば配線ミスがあってレールの番号を変えたくなったら,「new rail("00")」のところだけ変えれば,残りはそのままで大丈夫です。
さて,これだけなら配列変数でも良さそうですが,クラスを使うとコード部分も入れ替えることができます。
継承
たとえば,レールのプラスマイナスを逆に配線してしまい,プログラムの方で対応したいときは,どうしたらよいでしょう。このままでは車両が逆向きに走ってしまいますから,普通なら「$r0->drive(3);」の部分を全部マイナスにしないといけません。
こんなときオブジェクト指向では,railクラスの後に,以下のような新しいクラスを追加します。
リスト3 revrailクラス
class revrail extends rail {
function drive($speed) {
parent::drive(-$speed);
}
}
短いわりにはわかりにくいリストですが,やっていることはシンプルです。「class revrail extends rail」の部分で,railクラスを拡張してrevrailにすることを宣言しています。内容はdriveメソッドの変更で,引数$speedをマイナスにして,親であるrailクラスのdriveメソッドに渡しています。
使い方は簡単で,さきほどの「$r0 =& rail("00");」のところを「$r0 =& revrail("00");」にするだけです。これで,$r0を使っているところは全部逆向きになります。もちろん,今まで通りrailクラスも使用でき,こちらは何も変わりません。
呼び出し方もrailクラスのときと同じです。「$r0->drive(3);」とすれば,$r0がrevrailクラスなら,-3にしてrailクラスに渡されることになります。使う側は,$r0がrailクラスなのかrevrailクラスなのかを知らなくてもまったく問題ありません。
これを応用すると,あるレールは別のコントローラにつながっているためコマンドが違うといった場合でも,スマートに対応することができます。新しいクラスであっても,railクラスと同じメソッドが用意されていれば,railクラスのかわりに使うことかできるためです。コントローラに送るコマンドはrailクラスの中だけで完結しているため,コマンドが変更されても,railクラスの定義を変えるだけで済むのです。
なお,オブジェクト指向の用語では,「revrailクラスはrailクラスを継承している」「revrailクラスではdriveメソッドをオーバーライドしている」などと呼んでいます。また,Javaのような厳密な型宣言を必要とする言語では,たとえrailクラスと同じメソッドを持っていても,railクラスを継承していない限りrailクラスのかわりに利用することはできません。

