StrategyパターンとSQL文の構築

著者:杉浦

StrategyパターンとSQL文の構築

 Strategyパターンはコーディングのやり方の一つです。
Strategy パターン – Wikipedia
 
 画像は増補改訂版 Java言語で学ぶデザインパターン入門 | 結城 浩 | コンピュータ・IT | Kindleストア | Amazonより引用。
 Strategyパターンはとある目的を達成するためのアルゴリズムをごっそり代えることを容易にするプログラミングパターンです。業務においては仕様がごっそり変わっても、コードへの影響を小さく抑えるために用います。場当たり的な変更を繰り返すと仕様が変わって、関係を持つクラスやメソッドが変わって、更に共通箇所が変わって、変わった共通箇所に合わせて全く関係ない場所が変わって……なんてことも起き得ます。Strategyパターンを実現する方法の一つは、変わることが予想される領域をあらかじめまとめて、まとめた部分の入出力の型を固定する、というものです。変わることが予想される領域は主に業務知識(八百屋さんのプログラムなら八百屋さんの行う業務についての知識)によって把握します。業務知識に重点を置く設計をドメイン駆動設計と言い、ドメイン駆動設計の説明はエリック・エヴァンスのドメイン駆動設計 | Eric Evans, 和智右桂, 牧野祐子, 今関剛 | 工学 | Kindleストア | Amazonに詳しく載っているらしいです。私は買ったはいいですが未だに半分も理解できていません。
 ドメイン設計ほど大仰でなくとも簡単にStrategyパターンを用いることのできる部分があります。それがSQL文の構築です。データベースからデータを取得する時が特に典型です。データ取得のSQL文の中身は、任意の種類の値を任意の条件で任意の順で取得する、という言葉で抽象化できます。種類がselect, join, group by句あたり、条件がwhrere, join句あたり、順がorder by句になりやすいです。joinが二つあるのは関係するデータを引っ張ってくる理由に見たい、絞り込みたいになりやすいからです。見えない値による順番で値を引っ張ってきたいことはレアです。例えば次の様にクエリ構築をまとめた関数を作れます。

/** 次の様にニュースモデルに記述 */
public function scopeNewsListSelectQuery(QueryBuilder $query){
    return // 表示したいカラム、計算結果等を決定する部分を構築するクエリビルダの集合
}

public function scopeNewsListWhereQuery(QueryBuilder $query){
    return // 表示したいデータの条件を決定する部分を構築するクエリビルダの集合
}

public function scopeNewsListOrderQuery(QueryBuilder $query){
    return // 表示したいデータの順を決定する部分を構築するクエリビルダの集合
}
/** 次の様にコントローラからクエリの集合体メソッドを呼び出し */
$news_list = query()->scopeNewsListSelectQuery()
                    ->scopeNewsListWhereQuery()
                    ->scopeNewsListOrderQuery()
                    ->get();

 これだけでは一見、単にクエリを分解しただけに見えます。実際は大きな変更や追記が必要になった時、次の様に部分的にまとまりを入れ替えることで変更部を減らせます。
 例えば、ニュースと同じ仕組みでレビューを表示したい、どのニュースとレビューを表示するかはif文で記述すると複雑になるくらい微細に違う条件で決める、となった場合は次の様にできます。

/** 次の様にページモデル(ニュースモデルを抽象化したモデル)に記述 */
public function scopePageListSelectQuery(QueryBuilder $query){
    return // 表示したいカラム、計算結果等を決定する部分を構築するクエリビルダの集合
}

public function scopeNewsListWhereQuery(QueryBuilder $query){
    return // 表示したいニュースの条件を決定する部分を構築するクエリビルダの集合
}

public function scopeReviewListWhereQuery(QueryBuilder $query){
    return // 表示したいレビューの条件を決定する部分を構築するクエリビルダの集合
}

public function scopePageListOrderQuery(QueryBuilder $query){
    return // 表示したいデータの順を決定する部分を構築するクエリビルダの集合
}
/** 次の様にコントローラからクエリの集合体メソッドを呼び出し */
$news_list = query()->scopePageListSelectQuery()
                    ->scopeNewsListWhereQuery()
                    ->scopePageListOrderQuery()
                    ->get();
$review_list = query()->scopePageListSelectQuery()
                    ->scopeReviewListWhereQuery()
                    ->scopePageListOrderQuery()
                    ->get();

 すっきりです。さらに突飛な要求が多く来たとしてもページという枠組みに収まっているならば、そうそう困りません。もし工夫なく作ろうとした場合、変更時に漏れのありがちなコピペコードになるか、複雑で読み難いうえにインデックスも張りにくい謎クエリビルディングをすることになります。
 もし、SelectもOrderも違うとしたら、それはそれで入力部と表示部は同じだがクエリはごっそり代わるというStrategyパターンにするべきでしょう。

  • この記事いいね! (1)

著者について

杉浦 administrator