#Setter/Getterパターンとはなんぞや
Setter/Getterパターンとは、クラスのフィールドの公開範囲をprivateにし、Setter/Getterと呼ばれるアクセッサメソッドでラップするパターン。
#基本形
class Cat {
// 名前
private $name;
// getter
public function getName() {
return $this->name;
}
// setter
public function setName($name) {
$this->name = $name;
}
}
$tarou = new Cat();
$tarou->setName('太郎');
echo $tarou->getName(); //太郎
#何ができるの便利なの?
class Cat {
// 名前
private $name;
// getter
public function getName() {
return $this->name . 'ちゃん';
}
// setter
public function setName($name) {
$this->name = $name;
}
}
$tarou = new Cat();
echo $tarou->setName('太郎');
echo $tarou->getName(); // 太郎ちゃん
アクセッサ内に処理を追加することによって、値を加工することができます。
##値の検査。
class Cat {
// 名前
private $name;
// getter
public function getName() {
return $this->name;
}
// setter
public function setName($name) {
// $nameが文字列型か検査
if(!is_string($name)){
throw new LogicException('文字以外で名前をつけちゃダメにゃ');
}
$this->name = $name;
}
}
$tarou = new Cat();
$tarou->setName(0120444444); // Fatal error: Uncaught exception 'LogicException' with message '文字以外で名前をつけちゃダメにゃ'
setter内に型チェックを追加することにより、$name
に想定外の型の値が入ってしまうことを防ぐことができます。
##フィールドのアクセス制御。
class Cat {
// 名前
private $name;
// コンストラクタ
public function __construct($name){
$this->name = $name;
}
// getter
public function getName() {
return $this->name;
}
}
$tarou = new Cat('太郎');
echo $tarou->getName(); // 太郎
上記の$tarou
は、インスタンス化以降$name
を外部から設定する手段がありません。すなわち$name
は常に'太郎'であることが保証されます。
##自己カプセル化
class Cat {
// 名前
private $name;
// getter
public function getName() {
// 生類憐れみの令施行により、すべての猫は様付けで呼ばなければならない。
return $this->name . '様';
}
// setter
public function setName($name) {
$this->name = $name;
}
public function run(){
return $this->getName() . '「走るにゃ!」';
}
public function fly() {
return $this->getName() . '「飛ぶにゃ!」';
}
public function sleep() {
return $this->getName() . '「zzz...」';
}
}
$tarou = new Cat();
echo $tarou->setName('太郎');
echo $tarou->run(); // 太郎様「走るにゃ!」
echo $tarou->fly(); // 太郎様「飛ぶにゃ!」
echo $tarou->sleep(); // 太郎様「zzz...」
自身のフィールドに対してもアクセッサを経由することによって、フィールドとの結合を緩めることができます。
もし政令が撤廃され様付けが不要になったとき、各メソッドで$this->name
を直接記述していた場合、すべての$this->name
を使用している箇所を書き換えなければなりません。
上記の例だと、getName()
内の「 . '様'」を取り除くだけで対応できます。
#printfデバッグ
class Cat {
// 名前
private $name;
// getter
public function getName() {
// スタックトレースを表示
debug_print_backtrace();
return $this->name;
}
// setter
public function setName($name) {
$this->name = $name;
}
public function look(){
return $this->name . '「見るにゃ!」';
}
public function listen() {
return $this->getName() . '「聞くにゃ!」';
}
public function say() {
return $this->name . '「言うにゃ!」';
}
}
$tarou = new Cat();
echo $tarou->setName('太郎');
echo $tarou->look(); // 太郎「見るにゃ!」
echo $tarou->listen(); // 太郎「聞くにゃ!」#0 Cat->getName() called at [cat.php:24] #1 Cat->listen() called at [cat.php:36]
echo $tarou->say(); // 太郎「言うにゃ!」
setter/getter内にスタックトレース表示用関数を設置する等すれば、ログの追跡が容易になります。
上記の例では、各メソッドのうち、listen()
から利用されていることがすぐにわかります。
#遅延初期化
class Cat {
// 名前
private $name;
// getter
public function getName() {
if(!is_null($this->name)){
return $this->name;
}
// 重い処理
for($i=0; $i<1000000; $i++){
$cat = '太郎';
unset($cat);
}
$cat = '100万回死んだ太郎';
$this->name = $cat;
return $this->name;
}
}
$tarou = new Cat();
echo $tarou->getName(); // 100万回死んだ太郎
重い値の生成処理があるとき、その値が実際に必要になるまで生成処理を遅延することができます。