だいぶ前に勉強会で学んだ内容のメモを見つけたので、復習がてらまとめる。
かなり前の話なので、解釈等が間違っているかもしれない。
(特にValueObject以下)
題材
問い合わせフォームをつくる。
クラスの役割
クラスの役割は次の2つがある。
1. データの保持
クラスにプロパティ
public $name
を生やすことで、配列と違い 明示的に「どんな項目が必要か」を示す ことができる。
つまり、データの持ち方の仕様を決めることが可能。
例えば、下記のようなUserクラスの場合
class User {
public $name;
public $age;
public $company;
}
「このクラスには $name
$age
$company
項目が必要」ということになる。
2. 処理のモジュール化
バリデートなど、処理のモジュール化としてクラスを作ることもある。
この場合、クラス作成時に必ず 「何をするクラスか」をコメントなどで書いておく ことがポイント。
処理をモジュール化したクラスを作ったら
$contact = new User; //User:クラス名
でクラスのインスタンスができる。
インスタンスを作ることで実装したメソドが ->
で呼び出せるようになってとても便利。
クラスの外部化
ここまでを「クラスを外部化」というらしい。
ただし、
return $contract->validate();
と ->
で呼び出せるようになったところで、これだとちょっと外部化しただけ。
クラス作成のポイント
1クラス1処理が鉄則。
例えば以下のような処理を行いたい場合、
- リクエストデータをとってくる
- バリデーションをする
- セッションに突っ込む
- DBに突っ込む
4つ全ての処理を1つのクラスに担わせるのはナンセンス。
クラスをつくる上で大事な概念
- 1クラス1責務
- IPO
この2つを意識することがとても大切。
クラスをIPOに当てはめると、以下のようになる。
- I:Input...引数
- P:Process...1クラス1責務
- O:Output...戻り値、例外
返り値の重要性
「何をするのか」ではなく 「何をして何を返すのか」 が一番大事。
保守の時に返すものを修正することはよくあるので、最初からドキュメント化しておくべき。
php7から使える返り値の型指定
validate():bool
メソドの後ろに :型
と書くと、返す型を指定することができるので積極的に使おう。
バリデーションの返り値
バリデーションの場合は「OK」or「NG」だけを返すのではなく、エラーメッセージが出せるようにしておくこと。
ただし、「sessionにエラーメッセージを返す」といったところまでもバリデーションクラスに書いてしまうとクラスの趣旨がずれてしまうので注意する。
バリデーションクラスは 「バリデーションオブジェクトをつくる」 ことに専念させ、受取手が好きに扱えるようにするスタンスがよい。
Serviceクラスを作り、そこでDBに保存させる、とか。
コントローラの役割
-
return view
する - リダイレクトする
- etc...
といったように、リクエストの処理をちゃんと行うことがコントローラの役割。
ちなみにコントローラ内でsessionを使った場合、デバッグ用のartisanコマンドでは使えなくなるので注意。
(sessionはHTTP経由でないと使えないため)
Serviceを作る場所
Laravelとかだとserviceは app
の下に作るのが一般的っぽい。
ValueObject
バリデーションオブジェクトをそのままもらうだけなのはやな感じなので、 ValueObject(以下、VO) にあたるContactクラスをつくるのがおすすめ。
VOをつくることで、配列とは違い「ここを見ればどんな項目が入ってくるのか」が確実にわかるようになる。
$contact = new Contact();
$contact->name = $data["name"]; //安全性が保障されている
ステートフル/レスのどちらが理想か?
プロパティとメソドのどちらも持たせるクラスのことを「ステートフル」と言えるかもしれない。
こうするとそのクラスを見てる時はラクだが、依存しあっている(プロパティとメソドが?)と間違った処理を書いてしまった時に大変。
なので、クラスの中で依存しあっていない「ステートレス」が理想的。
クラスは状態・処理の両方を持たせることができるが、そうしてしまうと複雑度が増す&依存性があがるので避けるべき。
ステートレスなクラスの例をあげるとするならLaravelのEloquent。
Eloquentを自分で作ってはいけない。
まとめ
クラスを作る時には
- 処理をするためのクラスか
- データを保持するためのクラスか
を考えるようにしましょう。