PHPなオブジェクト指向入門 vol.3(前半)

今回は前回までと同様に肥満度チェックアプリを題材に「継承」について書いてみます。また、「継承」と合わせて複雑なWEBアプリケーション構築において必須となるMVCモデリングソースにもなるように心掛けました。


さて、「継承とは...」といったどこにでも書いてあることや構文はお手持ちの書籍に譲るとして、大事だと思うことだけ。

基本的にスーパークラス(親クラス)よりサブクラス(子クラス)の方が多機能なクラスです![※1]
PHPでは多重継承ができない。すなわち、一度に一つのクラスの拡張しかできない。[※2]
クラスの機能を拡張したいだけなら、必ずしも継承を使わなくて良いことも多い。[※3]
・PHP4では抽象クラスを定義し、それを継承することはできません。[※4]


[※1] 「スーパーとサブ」or「親と子」という言葉のイメージが、はじめて学ぶときには障害となっている気がします。
[※2] “一子相伝”とは呼ばず単一継承と呼びましょう。
[※3] オブジェクトコンポジションと呼ばれる手法。Javaではおなじみの手法ながら、PHPでは意外なほど知られていない。
[※4] そもそもPHP4では抽象クラス・インターフェースといったOOPの中核機能がないので(PHP5はあり)。


では、さっそくサンプルソースを。ァイル構成は以下の4ファイルとなります。強引に継承、MVCサンプルソースとしているので一見すると複雑に見えますが、POSTされた身長と体重から肥満度と判定を表示するだけのごく簡単なアプリケーションです。サンプルはコピペしていただければ動くハズなので、動ごかしてみてください。ただし、実際の運用ではなく勉強用サンプルなので煩雑なエラー処理等は省略しています。また、動作確認もほとんどしてません。


・index.php:実際にユーザがアクセスするファイル。入力フォームや肥満度の解析結果を表示。
・template.tpl:共通テンプレート。(セキュリティの観点からは template.tpl.php とした方がよいです。)
・CheckBMI.class.phpスーパークラス(親クラス)。身長と体重から肥満度を計算するだけ。
・CheckBMI_Plus.class.php:サブクラス。肥満度の計算の他に判定機能も実装。アプリにおいて使用するのはこのクラス。
・おまけファイル:CheckBMI_Plus2.class.php:オブジェクトコンポジションによりCheckBMI_Plus.class.phpと同等の機能のクラスを定義。


index.php

<?php
/**
 * ユーザーはこのファイルにアクセスします。
 * このスクリプトはMVCモデリングにおけるControllerに相当。(POSTデータ値の有無で制御)
 * MVCではアプリケーションをロジック(Model), 表示(View), 制御(Controller)に分離して構築。
 * =>メリット:ソースの可読性、保守性などが向上。デザインとロジックの分業が容易になる。クラスを使う人、作る人といったようにプログラマー間分業も容易になる。 etc.
 * =>デメリット:PHPの特徴でもあるHTMLとのごった煮スタイルではない。MVCについて書いた書籍が少ない。簡単なアプリケーションの場合にはファイルが増えるだけ。MVCになれるとごった煮スタイルが恐ろしく汚く見える。 etc.
 * PHPではVeiwの実装にSmartyを使う場合が多いですが、ここでは簡単化のためインクルードにより表示。
 */

include('CheckBMI_Plus.class.php');//ロジッククラス(Model)をインクルード(通常ロジックは別ファイルにします。)
$htmlmsg = "";//出力内容の一部を格納する変数
if(!empty($_POST['tall']) && !empty($_POST['weight'])){// POSTされたとき => BMI計算結果を表示
	$obj_BMI_plus = new CheckBMI_Plus($_POST['tall'], $_POST['weight']);
	if($obj_BMI_plus->IsError() === false){
		$htmlmsg .= "身長".$obj_BMI_plus->tall."メートル、体重".$obj_BMI_plus->weight."キログラム。";
		$htmlmsg .= "<br>そんなあなたのBMIは ".$obj_BMI_plus->getBMI()."です。";
		$htmlmsg .= "<br>判定:".$obj_BMI_plus->JudgeBMI();
	}else{
		foreach($obj_BMI_plus->IsError() as $val){
			$htmlmsg .= $val."<br>";//エラーメッセージ
		}
	}
	include('template.tpl');// View
}else{// デフォルト => 入力フォームを表示
	$htmlmsg .= "身長[メートル]と体重[キログラム]を入力してください。";
	include('template.tpl');// View
}
?>


続いてテンプレートファイル。このアプリは非常にシンプルなので、ひとつのテンプレートを使い回すことにします。


template.tpl

<html>
<head><title>肥満度チェック</title></head>
<body>
<div>
<p>身長と体重を入力してください。</p>
<form action="" method="post"><!-- action="" はスクリプト自身にPOSTすることを意味する -->
身長:<input type="text" name="tall"> [m]<br>
体重:<input type="text" name="weight"> [kg]<br>
<input type="submit" value="肥満度をチェック">
</form>
</div>
<?php if(!empty($htmlmsg)) echo "<div>".$htmlmsg."</div>"; ?>
</body>
</html>


index.php はPOSTデータがあるときには、肥満度を計算後、template.tpl を読み込んで結果を表示します。POSTデータのないときは単に template.tpl を読み込んで入力フォームを表示するだけです。


肥満度の計算はすべて CheckBMI_Plus クラスに放り込めば結果が返ってきます。ロジックとデザインのごった煮スタイルだと index.php は何十行にも及ぶでしょうが、たったの数十行です。


続いて、本題である継承を使った CheckBMI_Plus クラスのソースを。(つづく)


[追記]
このページの閲覧数が多いようなので追記。

・ここでは簡単のため include でテンプレートを読み込んで表示(VIEW)していますが、フレームワークを使った開発では include(); ではなく、あらかじめ表示用の変数に割り当てたりします。

・フレームワークを用いるとコントローラは複雑かつ高度に作られているので、この例のように if などで原始的に振り分けるといったことはしません。

※この文を書いたころは Zend Framework も symfony もなかった頃なので、現在のフレームワーク駆動の MVC とは多少異なります。