Hello World の次は、短縮したい URL を入力する画面が要る。get '/' => 'index' の1行と、__DATA__ に埋め込んだ EP テンプレートで POST 送信のフォームを出す。送信先はまだ無いので、押すと Page not found になるところまでです。
SNS に貼る URL が長すぎると友人に相談され、URL 短縮サービスを Perl で作ることにした。Mojolicious::Lite を cpanm で入れ、数行の app.pl を morbo で起動して Hello World を表示するまで。Web アプリを作るのが初めての方向けです。
add・list・complete を if-elsif で書いた約50行の Todo CLI を、Moo の Task クラス、JSON 保存、Repository、ファイルなしのテスト、Command、Getopt::Long と1回に1つずつ作り直し、SQLite へ差し替える演習で締める全10回の案内板です。
Repository パターンの応用演習です。TaskRepository::Role を適用した TaskRepository::SQLite を DBI と DBD::SQLite で書き、環境変数やオプションで JSON と切り替える。Task クラスも Command クラスも変えずに済むことを確かめます。
第1回の約50行の if-elsif から、Task・Repository・Command のクラスとロールに育った Todo CLI を見比べる。コードは増えたのに、コマンドの追加や保存先の差し替えが楽になった理由を、SRP・OCP・DIP の3つの設計原則で整理します。
shift @ARGV でサブコマンドと引数を取り出す作りでは、--verbose や --file のようなオプションを足しにくく、引数の順番に縛られる。Perl 標準の Getopt::Long でグローバルオプションと --help を解析し、サブコマンドと組み合わせます。
Command パターンにしたメイン処理へ、タスクを完了にする complete を足す。Command::Complete クラスを書き、コマンドマップに1行足すだけで、Command::Add と Command::List には手を入れずに済むことを確かめます。発展課題は Command::Delete です。
delete・edit・search とコマンドを足すたびに、メイン処理の if-elsif が伸び、コマンドごとのテストも書けない。Command::Role で execute と description を約束し、Command::Add と Command::List をクラスに切り出して、コマンド名からハッシュで引き当てます。
TaskRepository::File でテストを書くと、テストのたびにファイルができ、失敗すると後始末が漏れ、並列実行では取り合いになる。同じロールを適用した TaskRepository::InMemory を作り、Test::More のテストをファイルなしで回します。
add・list・complete のどの分岐にも load_tasks と save_tasks が混ざり、保存形式を変えるとメイン処理まで確かめ直すことになる。TaskRepository::Role でインターフェースを決め、TaskRepository::File に JSON の読み書きを集めます。
タブ区切りの保存形式は、タスク名にタブが入ると崩れ、列を足すたびに順番を覚えておく必要がある。Perl の JSON モジュールの encode_json と decode_json で Task オブジェクトを読み書きし、UTF-8 の扱いと整形出力まで押さえます。
文字列の配列で管理していたタスクには、完了状態や優先度を持たせる場所がない。Moo で id・title・is_done を持つ Task クラスを作り、mark_done() で完了にする。保存はタブ区切りのテキストファイルに変えます。
Perl で add・list・complete の3つのサブコマンドを持つ Todo CLI を、@ARGV と if-elsif とテキストファイルだけで作る。約50行で動くが、コマンドを足すたびに分岐が伸び、ファイルを読む処理が重複し、テストも書けない。その限界をコードで確かめます。
機能を足すたびに if/elsif が伸びる掲示板の振り分けを、ハッシュ、コードリファレンス、ハンドラークラス、Moo::Role、委譲、レジストリ、ルーティングと1回に1つずつ作り直し、最後に Strategy パターンだったと種明かしする全12回の案内板です。
if/elsif をなくそうと作ってきた Dispatcher と Handler ロールは、Strategy パターンそのものだった。Context・Strategy・Concrete Strategy の3つに、このシリーズで作ったクラスを対応させる完結回。次に読むとよい Command・Factory Method・Chain of Responsibility も紹介します。