[Laravel] Strategy パターンを使ってみる
GoF 初挑戦。
プロジェクトの概要
- 自作の Laravel パッケージ
- データベースのテーブル構成からモデル、コントローラー、ビューを自動生成する
現状
処理の共通化を目指すあまり、モデル、コントローラ、ビューの生成処理をすべて generate()
に詰め込み、生成したいファイルを引数で指定して、メソッドの中で分岐するようにしている。
$generator = new Generator();
$generator->generate('model');
class Generator
{
public function generate($type)
{
if ($type === 'model') {
// モデルの生成
} elseif ($type === 'controller') {
// コントローラの生成
} elseif ($type === 'view') {
// ビューの生成
}
}
// 各生成処理で使うメソッドがたくさん
private function buildForm() {}
private function buildRelations() {}
}
課題
Generator
とgenerate()
が巨大化した- 目的のメソッドを見つけるのが大変
- どの private メソッドがどの public メソッドで使われているのかわからない
- 機能追加も修正も時間がかかってしまう
やりたいこと
- クラスを分割して見通しをよくする
- 機能追加や修正をスムーズにする
モデル・コントローラ・ビューの生成処理は、流れはすべて同じで、生成処理がだけが違っていたので、Strategy パターンを使うことにした。
改善前のクラス構成
Generator
の中に生成処理がすべて詰まっている。
Generator
を使うのが GenerateCommand
クラス。
src
├── Console
│ └── GenerateCommand.php
├── Generator.php
└── ServiceProvider.php
改善後のクラス構成
src
├── Console
│ └── GenerateCommand.php
├── ControllerStrategy.php
├── Strategy.php
├── ModelStrategy.php
├── Generator.php
├── ServiceProvider.php
├── Table.php
├── ValidationStrategy.php
└── ViewStrategy.php
Generator
を次の 3 つに分割した。
ModelStrategy
ControllerStrategy
ViewStrategy
各クラスには、各生成処理で使うものだけを含ませる。
たとえば、ModelStrategy
にはモデルの生成処理が使うメソッドだけを含ませる。
GenerateCommand
では、次のようにして生成処理を呼び出す。
呼び出すメソッドはすべて generate()
だけど、インスタンスを生成する際に渡すクラスによってその処理内容が決まる。
$generator = new Generator(new ModelStrategy);
$generator->generate(); // Modelの生成処理
$generator = new Generator(new ControllerStrategy);
$generator->generate(); // Controllerの生成処理
$generator = new Generator(new ViewStrategy);
$generator->generate(); // Viewの生成処理
なぜこんなことができるかというと、generate()
は受け取ったインスタンスの generate()
を実行するようにしているから。
class Generator
{
private $strategy;
public function __construct(Strategy $strategy)
{
$this->strategy = $strategy;
}
public function generate()
{
$this->strategy->generate();
}
}
ちなみに、Strategy
はこんな感じ。
interface Strategy
{
public function generate();
}
ModelStrategy
はこんな感じ。
class ModelStrategy implements Strategy
{
public function generate()
{
// モデルの生成処理
}
// モデルの生成処理だけで使うメソッドなど
private buildRelations() {}
}
まとめ
Strategy とは「戦略」という意味。 同じ問題を解くにも、いろんな解き方 (戦略) がある。
Strategy パターンは、同じ処理をいろんな方法で実現するときに、いろんな戦略を簡単に切り替えられるようにするためのデザインパターン。
たとえば今回なら、「ファイルを生成する」という共通の問題があり、 それに対して「モデル」「コントローラ」「ビュー」という異なる戦略があった。
そしてデザインパターンを活用することによって、ファイルを生成する流れは共通化しながらも、 戦略を切り替えることによって生成するファイルを簡単に選択できるようになった。