Setter/Getterパターンについて覚え書く

#Setter/Getterパターンとはなんぞや
Setter/Getterパターンとは、クラスのフィールドの公開範囲をprivateにし、Setter/Getterと呼ばれるアクセッサメソッドでラップするパターン。

#基本形

```php
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(); //太郎
```

#何ができるの便利なの？

```php
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(); // 太郎ちゃん
```

アクセッサ内に処理を追加することによって、値を加工することができます。


##値の検査。

```php

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`に想定外の型の値が入ってしまうことを防ぐことができます。

##フィールドのアクセス制御。

```php
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`は常に'太郎'であることが保証されます。

##自己カプセル化

```php
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デバッグ

```php
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()`から利用されていることがすぐにわかります。

#遅延初期化

```php
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万回死んだ太郎
```

重い値の生成処理があるとき、その値が実際に必要になるまで生成処理を遅延することができます。