
動的なサイトを制作する場合、私はPHPでプログラムを組むのですが、少し悩みがあります。
それは、
クラスがとても長くなると見にくい。
ということです。
クラス以外のソースであれば、PHPファイルを分割し適度な長さにします。
ですが、クラスは範囲指定があるため、分割できません。
そのため、クラス内の処理が増えてくると、ソースがどんどん長くなっていきます。
長くなると、とても見にくくなります。
後からメンテナンスすることを考えると、長いソースはメンテがしにくいです。
だから、分割したいのですが、クラスは分割できない。
というジレンマに陥りました。
ところが色々調べてみると、間接的な方法ではありますが、クラスを分割する方法がありました。
備忘録として記録します。
クラスを分割する原理
とはいえどうやればいいの?
クラスそのものは、別ファイルに術できません。
1クラス1ファイルの原則があるためです。
そこで、別のファイルに書いた関数群を読み込むのです。
そして、読み込んだ関数群は、クラスの関数として使用できます。
別のファイルに書いた関数を、クラスの関数として使用できるということは、
事実上クラスファイルの分割になります。
事実、この方法で長すぎるクラスを、ファイル分割することができました。
具体的な記述方法
クラスに読み込む場合、通常の関数記述だけでは読み込みできません。
読み込めるようにする文法があります。
それがこちらです。
分割するファイル側の記述(読み込まれるファイル)
■trait_b.php
<?php
trait b
{
function trait_sample()
{
echo "トレイト側で出力";
}
}
?>
呼び出す側の記述(クラス側の記述)
■class_a.php
<?php
require_once("trait_b.php");
class a
{
use b;
}
?>
クラスを呼び出す側の記述
<?php
require_once("class_a.php"); // クラス
$a = new a();
$a->trait_sample();
?>
実行結果
トレイト側で出力
クラスを分割する場合のルール
クラスで関数群を読み込むには、関数群を traitで囲います。
そして、そのtraitに名前を付けます。
上記サンプルだと、
trait b
と記述しているので、
「b」という名前の関数群となります。
クラス側では、2つのルールがあります。
一つは、require_once でファイルを読み込むことです。
外部ファイルなので、これはtraitに限らず必要なことです。
もう一つがuse を記述することです。
上記サンプルだと、
use b;
と記述しています。
これは、trait bをこのクラスで使用するという意味です。
あとは、クラスに記述した関数として呼び出すことができます。
サンプルでは、
$a = new a();
$a->trait_sample();
と記述しています。
class aには関数が一つもありませんが、trait bの関数が呼ばれています。
疑問 traitって何?
このやり方を知った時に、真っ先に「traitって何?」
と思いました。
知らない記述方法です。
調べてみると以下のようなことでした。
- 汎用的な関数の集合体
- クラスからのみ呼び出せる(useできる)
つまり、クラス専用の呼び出しパーツです。
なので、クラス以外では呼び出せません。
まさに、分割記述したいケースにぴったりのやり方です。
面倒ならば、クラスにまとめて書くのもよし。
クラスが長くてわかりにくいならば、traitで分割するのもよし。
自分のやり方に合わせて使用すればよいのです。
そのほかの疑問も調べてみました。
traitの使い方はわかりましたが、いくつか疑問がわきました。
それらを調べてみました。
Q. trait側にクラスと同じ名前の関数を作ったらどうなる?
エラーにはなりませんが、呼び出すとクラス側の関数が優先して呼び出されました。
Q. trait側にpraivate等で同じ変数名を作ったらどうなる?
重複エラーになりました。
Q. traitを呼び出している(use指定している)クラスを継承した場合、trait側の関数は呼び出せるか?
以下のようなクラスaを継承したclass c側から呼び出せました。
trait_b.php
<?php
trait b
{
function trait_sample()
{
echo "トレイト側で出力";
}
}
?>
class_a.php
<?php
require_once("trait_b.php");
class a
{
use b;
}
class c extends a
{
}
?>
呼び出す側
<?php
require_once("class_a.php"); // クラス
$c = new c();
$c->trait_sample();
?>
Q. 継承しているクラス側でも同じtraitを呼び出したらどうなる?
エラーにはなりませんでした。
ですが、推奨されない使い方のようです。
<?php
require_once("trait_b.php");
class a
{
use b;
}
class c extends a
{
use b;
}
?>
Q. 複数のtraitを呼び出す場合、それぞれのtraitに同じ関数名があるとどうなる?
試したら、エラーになりました。
クラスとtraitで同じ関数名はエラーになりませんが、trait同士ならばエラーになります。
trait b
{
function trait_sample()
{
echo "トレイトb側で出力";
}
}
trait d
{
function trait_sample()
{
echo "トレイトd側で出力";
}
}
Q. trait側で記述したprivate等の変数は、クラス側で参照できるか?
できました。
ですが、あまり推奨されないようです。
参照する場合は、trait側に以下のような変数を参照する関数を作るのがセオリーということでした。
trait b
{
private $b = "traitの変数";
function b_value()
{
return $this->b;
}
}
traitを使う場合にこうしたほうが良いというルール
実際にtraitを使ってみたところ、以下のようにしたほうが良いと思う点がありました。
- 関数名や変数名には、trait名を頭につける。
- traitの変数はクラス側でダイレクトに参照しない。
クラスの利点は、同じ関数名や変数名を使っても、クラス名さえ違っていれば問題ないという点です。
ですが、traitでは重複名称は使わないに越したことはありません。
特に、複数のクラスから呼び出せる以上、単一名称にしておくほうが無難です。
そこで、trait名を頭につければ重複することはなくなります。
なぜならば、trait名は重複すると文法エラー人るためです。
例えば、trait b という名称ならば、
praivate b_value;
function b_disp()
{
}
というように、trait名称をつけておけば、traitの再利用の際に、名称重複エラーを心配しなくてもよくなります。
trait側で作成した変数は、システム的にはクラス側でダイレクト参照できますが、以下の理由で推奨されないということでした。
trait側で、変数名を変えた場合、使用しているクラスすべて修正しなければいけない
traitは汎用的に呼び出されるため、traitは独立性を高めておいたほうがよいということです。
クラスはその辺気にしなくてもよかったのですが、traitでは気おつけなければいけませんでした。
ですが、クラスのファイル分割としてとらえれば、とても利便性が高いと感じました。