試験のPHP対象バージョンが「5.3」の中、5.4系で盛り込まれた仕様のお話で恐縮ではありますが。
今回から数回にわたって、トレイト(trait)についてお話をさせていただければ、と思います。
traitとは、特徴や特色、特性の事を言いまして、文脈によっては「習性」「体質」「素質」
なんて訳されかたをされたりもします…なんていうボケはおいておきまして。
物凄く大雑把には、「多重継承」と呼ばれる系譜の、テクニックであったり技術であったり
技法であったり実装であったりするもの、のうちのひとつです。
今回の一連のコラムでは、まずはストレートな実例を。
そこからもうちょっと「概念的な」部分、そこから一瞬横道にそれて「どんな事が出来るのか」の
バリエーションを見つつ、「多重継承」についても、少し書いていきたいと思います(行動宣言)。
割と単純かつ使い勝手の良いところで「アクセサ」及び
「プロパティのtypoブロック」あたりがよいかと思いますので、実装をみつつ、
traitについて簡単に理解をしていきたいと思います。
コラム62でやりました「__callによるアクセサ」を、ふと思い出してみましょう。
public function __construct() {
$this->data_ = array();
$this->propertys_ = array (
'foo' => 1,
'bar' => 1,
);
}
public function __call($name,
$param) {
//
//
if ( (0 === strncmp($name, 'get_', 4))||(0 === strncmp($name, 'set_', 4)) ) {
list($type, $k) =
explode('_', $name, 2);
if (false === isset($this->propertys_[$k])) {
// XXX 適宜エラー処理
throw new Exception('
えらー');
}
// else
if ('set' === $type) {
$this->data_[$k] = $param[0];
} else {
return $this->data_
[$k];
}
} else {
// XXX 適宜エラー処理
throw new Exception('えらー');
}
}
//private:
private $propertys_;
private
$data_;
こんなんですね。
ちなみにpropertys_で「keyに変数名的なものを持っている」のは、単純に検索コストを考えての事です。
検索コスト周りについては、こーゆー感じのコードを書いてみると、何となく見えてくるかと思いますので参考まで。
えらいこと雑ですが(苦笑
$awk = array();
$s = 'a';
for($i = 0; $i < 1000; $i ++) {
$awk[] = $s ++; // 後置のインクリメン
ト。お仕事では使わないこと!
}
$awk2 = array_flip($awk);
// 値の線形検索
$t = microtime(true);
for($i = 0; $i < 100; $i ++) {
in_array("sf", $awk); // 1000個作ると真ん中が大体sfあたりなので
}
var_dump( microtime(true) - $t );
echo "\n";
// hash keyの
hash検索
$t = microtime(true);
for($i = 0; $i < 100; $i ++) {
$awk["sf"]; // 1000個作ると真ん中が大体sfあたりなので。hash検索
だから位置関係ないけど
}
var_dump( microtime(true) - $t );
echo "\n";
あるいはもうちょっと本質的には
「連想配列をハッシュ配列とも呼称するhashってのはそもそもhash検索から来ている」あたりを基点に
「ハッシュとは、ハッシュ関数とは、ハッシュ値とは、ハッシュ検索とは、なんぞや?」
という辺りを一度、きちんと学んでみるのもよいかもしれません。
いずれにしても、「valueの値を検索する」よりも「key/indexの値を検索する」ほうが、
実装的に”圧倒的にコストが安い”ってのは、把握/理解をしておくと便利です。
閑話休題
本題に戻しましょう。
さて、とあるクラス hoge を作成します。
(注:プロパティについて。行数を少し短くしたかったので直接初期化してますが、業務であれば、
コンストラクタなどで初期化した方がより丁寧かとは思います)
class hoge {
public function __call($name, $param) {
//
if ( (0 === strncmp($name, 'get_', 4))||(0 === strncmp($name, 'set_', 4)) ) {
list($type, $k) = explode('_', $name, 2);
if (false === isset($this->propertys_[$k])) {
// XXX 適宜エラー処理
throw new Exception('えらー');
}
// else
if ('set' === $type) {
$this->data_[$k] = $param[0];
} else {
return $this->data_[$k];
}
} else {
// XXX 適宜エラー処理
throw new Exception('えらー');
}
}
private $data_ =
array();
private $propertys_ = array (
'hoge_1' => 1,
'hoge_2' => 1,
);
}
別のクラスfooを作成します。
class foo {
public function __call($name, $param) {
//
if ( (0 === strncmp($name, 'get_', 4))||(0 ===
strncmp($name, 'set_', 4)) ) {
list($type, $k) = explode('_', $name, 2);
if (false === isset($this->propertys_[$k])) {
// XXX 適宜エラー処理
throw new Exception('えらー');
}
// else
if ('set' === $type) {
$this->data_[$k] =
$param[0];
} else {
return $this->data_[$k];
}
} else {
// XXX 適宜エラー処理
throw new Exception('え
らー');
}
}
private $data_ = array();
private $propertys_ = array (
'foo_1' => 1,
'foo_2' => 1,
);
}
勿論これくらいでしたら、__callあたりを親クラスに切り出して継承させてもよいのですが。
traitでも書けます、ってことで、実際に書いてみましょう。
trait accessor {
public function __call($name, $param) {
//
if ( (0 === strncmp($name, 'get_',
4))||(0 === strncmp($name, 'set_', 4)) ) {
list($type, $k) = explode('_', $name, 2);
if (false === isset($this-
>propertys_[$k])) {
// XXX 適宜エラー処理
throw new Exception('えらー');
}
// else
if ('set' === $type) {
$this->data_[$k] = $param[0];
} else {
return $this->data_[$k];
}
} else {
// XXX 適宜エラー処理
throw
new Exception('えらー');
}
}
private $data_ = array();
}
//
class foo {
//
use accessor;
//
private $propertys_ = array (
'foo_1' => 1,
'foo_2' => 1,
);
}
さて。一番基本的な書式をみていただいたところで。
次回、もう少し深い所に、ずずずいっと踏み込んでいってみましょう。