TOP比較データ> 例外処理の比較

徹底比較!! PHP & Java

第3回:PHP4とJavaのオブジェクト指向

著者:ワイズノット  土橋 芳孝   2004/12/6
前のページ  1  2  3
例外処理の比較

   企業システムにおける重要な機能のひとつに例外処理があります。例外処理とは本来想定していないエラーが発生した場合に、そのエラーに対応して実行される処理のことです。例外処理が実装されていない場合、エラーが発生した時の動作を予測できなくなるため、堅牢なアプリケーションを開発するうえで例外処理は不可欠な存在です。

   それでは例外処理を比較してみます。例外処理を比較するプログラムとして3つのファイルの内容を表示するプログラムを用意しました。各ファイルを開き、内容を表示したらファイルを閉じるという動作を3回繰り返しますが、2回目のファイルの内容を表示している途中で意図的にエラーを発生させています。
PHP4版  例外処理1
<?php
require_once 'PEAR.php';

function func1() {

// file1.txtを開く
if(($handle1 = fopen("file1.txt", "r")) == false) {
return PEAR::raiseError(
"file1.txtを開くことができません。");
} else {
echo "file1.txtを開きました。\n";
}

// file1.txtの内容を表示する
while(! feof($handle1)) {
echo fgets($handle1);
}

// file1.txtを閉じる
if($handle1 != false) {
if(fclose($handle1) == false) {
return PEAR::raiseError(
"file1.txtを閉じることができません。");
} else {
echo "file1.txtを閉じました。\n";
}
}
}

function func2() {

// file2.txtを開く
if(($handle2 = fopen("file2.txt", "r")) == false) {
return PEAR::raiseError(
"file2.txtを開くことができません。");
} else {
echo "file2.txtを開きました。\n";
}

// file2.txtの内容を表示する(途中でエラーが発生)
while(! feof($handle2)) {
echo fgets($handle2);
return PEAR::raiseError("エラー発生!!");
}

// file2.txtを閉じる
if($handle2 != false) {
if(fclose($handle2) == false) {
return PEAR::raiseError(
"file2.txtを閉じることができません。");
} else {
echo "file2.txtを閉じました。\n";
}
}
}

function func3() {

// file3.txtを開く
if(($handle3 = fopen("file3.txt", "r")) == false) {
return PEAR::raiseError(
"file3.txtを開くことができません。");
} else {
echo "file3.txtを開きました。\n";
}

// file3.txtの内容を表示する
while(! feof($handle3)) {
echo fgets($handle3);
}

// file3.txtを閉じる
if($handle3 != false) {
if(fclose($handle3) == false) {
return PEAR::raiseError(
"file3.txtを閉じることができません。");
} else {
echo "file3.txtを閉じました。\n";
}
}
}

// file1.txtの処理を実施する
$result = func1();
if(PEAR::isError($result)) {
print($result->getMessage()."\n");
}

// file2.txtの処理を実施する
$result = func2();
if(PEAR::isError($result)) {
print($result->getMessage()."\n");
}

// file3.txtの処理を実施する
$result = func3();
if(PEAR::isError($result)) {
print($result->getMessage()."\n");
}
?>

PHP4版  例外処理1の実行結果
file1.txtを開きました。
###########################
## file1.txtの内容 ##
###########################
file1.txtを閉じました。
file2.txtを開きました。
###########################
エラー発生!!
file3.txtを開きました。
###########################
## file3.txtの内容 ##
###########################
file3.txtを閉じました。

   一見、上記プログラムに問題はないように思えますが、実は大変な問題が潜んでいます。2回目に処理を実施したファイルfile2.txtの1行目を表示した後、意図的にエラーを発生させていますが、このエラーが発生したことでfile2.txtを閉じることができません。通常、ファイルを閉じなければ、そのファイルの内容は保証されませんし、メモリも無駄に消費してしまいますので、これは大きな問題だと言えます。この問題を解決するとプログラムは下記のようになります。

PHP4版  例外処理2
<?php
require_once 'PEAR.php';

$handle1 = false;
$handle2 = false;
$handle3 = false;

function func1() {
global $handle1;

// file1.txtを開く
if(($handle1 = fopen("file1.txt", "r")) == false) {
return PEAR::raiseError(
"file1.txtを開くことができません。");
} else {
echo "file1.txtを開きました。\n";
}

// file1.txtの内容を表示する
while(! feof($handle1)) {
echo fgets($handle1);
}
}

function func2() {
global $handle2;

// file2.txtを開く
if(($handle2 = fopen("file2.txt", "r")) == false) {
return PEAR::raiseError(
"file2.txtを開くことができません。");
} else {
echo "file2.txtを開きました。\n";
}

// file2.txtの内容を表示する(途中でエラーが発生)
while(! feof($handle2)) {
echo fgets($handle2);
return PEAR::raiseError("エラー発生!!");
}
}

function func3() {
global $handle3;

// file3.txtを開く
if(($handle3 = fopen("file3.txt", "r")) == false) {
return PEAR::raiseError(
"file3.txtを開くことができません。");
} else {
echo "file3.txtを開きました。\n";
}

// file3.txtの内容を表示する
while(! feof($handle3)) {
echo fgets($handle3);
}
}

// file1.txtの処理を実施する
$result = func1();
if(PEAR::isError($result)) {
echo $result->getMessage()."\n";
}

// file1.txtを閉じる
if($handle1 != false) {
if(fclose($handle1) == false) {
echo "file1.txtを閉じることができません。\n";
} else {
echo "file1.txtを閉じました。\n";
}
}

// file2.txtの処理を実施する
$result = func2();
if(PEAR::isError($result)) {
echo $result->getMessage()."\n";
}

// file2.txtを閉じる
if($handle2 != false) {
if(fclose($handle2) == false) {
echo "file2.txtを閉じることができません。\n";
} else {
echo "file2.txtを閉じました。\n";
}
}

// file3.txtの処理を実施する
$result = func3();
if(PEAR::isError($result)) {
echo $result->getMessage()."\n";
}

// file3.txtを閉じる
if($handle3 != false) {
if(fclose($handle3) == false) {
echo "file3.txtを閉じることができません。\n";
} else {
echo "file3.txtを閉じました。\n";
}
}
?>

PHP4版  例外処理2の実行結果
file1.txtを開きました。
###########################
## file1.txtの内容 ##
###########################
file1.txtを閉じました。
file2.txtを開きました。
###########################
エラー発生!!
file2.txtを閉じました。
file3.txtを開きました。
###########################
## file3.txtの内容 ##
###########################
file3.txtを閉じました。

   上記プログラムであれば、ファイルの内容を表示している最中にエラーが発生してもファイルを閉じることができます。しかし、上記プログラムにも問題がないわけではありません。ファイルを閉じるための処理を確実に実施するために、ファイルを処理するためのファンクション(関数)からファイルを閉じるためのコードを切り出しています。そのため機能の境界線が曖昧になり、コードの拡張性や再利用性が低下しています。

同様な処理をJavaで実現すると下記のようになります。

Java版  例外処理
import java.io.*;

public class RaiseErrorBat {
public static void main(String[] args) {

BufferedReader br = null;
String buf = null;

// file1.txtの処理を実施する
try {

// file1.txtを開く
br = new BufferedReader(
new FileReader("file1.txt"));
System.out.println("file1.txtを開きました。");

// file1.txtの内容を表示する
while((buf = br.readLine()) != null) {
System.out.println(buf);
}

} catch(IOException e1) {
System.out.println(e1);
} finally {

// file1.txtを閉じる
try {
if(br != null) {
br.close();
System.out.println(
"file1.txtを閉じました。");
}
} catch(IOException e2) {
System.out.println(e2);
}
}

// file2.txtの処理を実施する
try {

// file2.txtを開く
br = new BufferedReader(
new FileReader("file2.txt"));
System.out.println("file2.txtを開きました。");

// file2.txtの内容を表示する(途中でエラーが発生)
while((buf = br.readLine()) != null) {
System.out.println(buf);
throw new IOException("エラー発生!!");
}

} catch(IOException e1) {
System.out.println(e1);
} finally {

// file2.txtを閉じる
try {
if(br != null) {
br.close();
System.out.println(
"file2.txtを閉じました。");
}
} catch(IOException e2) {
System.out.println(e2);
}
}

// file3.txtの処理を実施する
try {

// file3.txtを開く
br = new BufferedReader(
new FileReader("file3.txt"));
System.out.println("file3.txtを開きました。");

// file3.txtの内容を表示する
while((buf = br.readLine()) != null) {
System.out.println(buf);
}

} catch(IOException e1) {
System.out.println(e1);
} finally {

// file3.txtを閉じる
try {
if(br != null) {
br.close();
System.out.println(
"file3.txtを閉じました。");
}
} catch(IOException e2) {
System.out.println(e2);
}
}
}
}

Java版  例外処理の実行結果
file1.txtを開きました。
###########################
## file1.txtの内容 ##
###########################
file1.txtを閉じました。
file2.txtを開きました。
###########################
java.io.IOException: エラー発生!!
file2.txtを閉じました。
file3.txtを開きました。
###########################
## file3.txtの内容 ##
###########################
file3.txtを閉じました。

   上記プログラム「Java版 例外処理」であれば確実にファイルを閉じることができます。また、機能の境界線が明確で拡張性や再利用性の高いプログラムを容易に実現できます。コードもはるかに読み易いものとなっています。

   Javaの例外処理は非常に強力な仕組みを備えており、開発者に例外処理の実装を強制します。そのため、開発者の意識やスキルに依存することなく例外処理を実現でき、強固なアーキテクチャを比較的容易に実現することが可能です。

   一方、強力な例外処理の仕組みを備えていないPHP4では、例外処理の実装について開発者の意識やスキルに頼らざるおえないため、強固なアーキテクチャを実現することが困難となります。また、PHP4で例外処理を実装すると上記プログラム「PHP4版 例外処理2」のようにオブジェクト指向プログラミングの実現が困難となり、コードの拡張性や再利用性を犠牲にすることになります。複雑に入り組んだ例外処理を必要とする大規模な企業システムにおいて、PHP4がJavaの代役を務めることは、やはり難しいのではないでしょうか。

   ここまで、PHP4とJavaのオブジェクト指向について比較してきました。やはり、堅牢なアーキテクチャやコードの拡張性、再利用性といった大規模企業システムに必須の要件がPHP4では満たされていないため、PHP4は企業システムにおいて伸び悩んでいると筆者は感じています。

   次回からはPHP5について考察していきます。PHP5はJavaにどこまで迫れるのでしょうか。ご期待ください。

前のページ  1  2  3



著者プロフィール
株式会社ワイズノット  土橋 芳孝
以前はJavaを利用したWebアプリケーション開発とオブジェクト指向設計を得意としていたが、ワイズノットに入社以来、PHPの魅力にとりつかれる。現在はワイズノットのプロジェクトマネージャーとして、PHPをはじめとしたオープンソースの普及に力を注いでいる。


INDEX
第3回:PHP4とJavaのオブジェクト指向
  オブジェクト指向とは
  Singletonパターンによる比較
例外処理の比較