最近のクラス設計はこんな感じ
ここ2ヶ月くらいに書いてるクラスは似たようなパターンになっているので、 メモしておきます。 一般化できるのかな。
DBにマスターデータがあって、memcacheにもキャッシュされてるよとか、 DBからデータを集計して、集計結果を別のテーブルに入れるよ、 みたいな状況で使います。
クラス分け
- 「ユーザのデータ」を表わすようなクラス。 この記事では主にこのクラスについて説明する。
- 「ユーザのデータが入っているテーブル」を表わすクラス。 DB, memcache, WebAPIなどの入出力ごとにクラスを作る。 memcacheとWebAPIはシステム全体で1種類のクラスがあればよくて、 memcacheについてはPHPであればPECLのMemcacheクラスでよい。 DBのクラスは都度作ることが多くて、それはSQL文をしまうため。
クラスのプロパティ
- 「ユーザのデータ」を格納するところ。
プロパティ変数に直接入れる(
$this->nickname
)みたいにするか、 配列に入れる($this->data['nickname']
)かどちらかで。 直接入れるときはmemcache等への格納のときに一手間かける必要があって、 配列に入れるときはGetterに一手間かける必要がある。 - DB, memcache, WebAPIなどの入出力は、クラスのプロパティ変数として持たせる。 外部から入れ替えることができるようにしておいて、 テストのときにモックを差し込む。 通常は外部からは操作しない。
各メソッド
これは次の順番に実装していきます。 値を返すメソッド以外はメソッドチェーンが使えるようにします。
- 入出力用のオブジェクトを入れ替えるメソッド。これらはテスト専用。
- GetterとSetter。これらはテスト専用のときもある。
- memcacheからデータを取り出したり、格納したり、
あるいは消去したりするメソッド。
取り出したデータはプロパティ変数に入れるので、
値を取り出すときはこんな感じ:
$foo->load()->nickname
- 同様にDBへの入出力を扱うメソッド。
- 同様にWebAPIへの入出力を扱うメソッド。
- DBから集計するようなメソッド。
- どこから取り出すかを透過的に行うメソッド。
memcacheにあればそれを、なければDBから、みたいにする。
これは
getInstance()
みたいなスタティックメソッドにすることが多くて、$foo = Klass::getInstance()
とすると $foo にはすでに値が入っている。 そうでなければ$foo = new Klass(); $foo->load()
とする必要があり、$foo->load()
を忘れるに決まっている。 - どこから取り出すかを透過的に行うGetter。
オブジェクトのプロパティにあればそれ、なければmemcacheから、
それでもなければDBから、みたいにする。
普通はひとつ上のメソッドと、どちらかを実装する。
ただし
getInstance()
は、中で new するだけであっても、 用意しておくと、のちのちの改変に強いし、 メソッドチェーンも使えて便利です。
この順番で実装するとテストがしやすいのです。
透過的なGetter
プロパティごとにどこから取り出すのかが変わる場合があって、 そのときは このプロパティを取得するのはどのメソッドか、というのを対応表にしておいて、
- プロパティに値があればそれを返す。 これは配列に要素を入れるパターンがよいと思う。
- memcacheから値を読み込んで、読み込めればその値を返す。 memcacheは上の配列をまるごと保存するのがよい。
- 対応表からメソッドを引いてきて、それを実行する。 その結果、プロパティ変数(配列)に値が入るから、 memcacheに保存して、値を返す。
と、こんな感じ。