Perl で自動販売機の動きを作りながら、状態の管理を組み直していく全10回の連載です。「待機中」と「コイン投入済み」を if/else で切り替えるところから始め、「払い出し中」「売り切れ」を足して分岐が膨らんだところで、状態ごとのクラス、共通のメソッドを決める Moo::Role、現在の状態を抱える VendingMachine(Context)へと組み直し、最後にそれが State パターンだと名前を付けます。
各回は前の回のコードに手を入れていく作りなので、通して読むなら第1回からが早道です。このページは目次を兼ねた案内板です。入口は「どこから読むか」で、困りごとが決まっているなら「困りごとから探す」で、順に追うなら「全話一覧」で選んでください。
対象は、Moo の has と sub でクラスを書け、Moo::Role と with を使ったことがある方です。コードは Perl v5.36 以降(サブルーチンシグネチャ)で書いています。Moo が初めてなら、先に前シリーズ「変数とサブルーチンだけで書いていたPerlを、Mooのクラスで整理する全12回」の目次(このページの最後にリンクがあります)から読んでください。
どこから読むか
- 第1回 — 状態を if/else で書くところから、設計が変わる順にたどりたいなら。第2回で、状態を足すと何が困るかを確かめる
- 第3回 — 状態をクラスに分ける形から入りたいなら。前提は第2回の、4つの状態を if/elsif で見分ける VendingMachine
- 第6回 — 状態クラス自身に遷移先を決めさせる形を先に見たいなら。前提は第5回の VendingMachine(Context)
- 第10回 — State パターンの構造と、Strategy パターンとの違いだけ知りたいなら。第9回の完成コードと対応表で照らし合わせる
困りごとから探す
状態が増えるたびに、if/else が伸びていく
- 第1回 — 「待機中」のときと「コイン投入済み」のときで、同じボタンの結果を変えたい
- 第2回 — 状態を1つ足すと、insert_coin も select_product も dispense も全部直すことになる
状態ごとのクラスに分けたが、まとまりがない
- 第3回 — if/elsif で状態を見分けるのをやめて、状態ごとにクラスを分けたい
- 第4回 — どの状態クラスにも同じメソッドを持たせたい。書き忘れを、呼び出す前に見つけたい
- 第5回 — 呼び出し側が状態オブジェクトを付け替えている。使う側に状態クラスを見せたくない
状態の切り替えを、安全に・足しやすくしたい
- 第6回 — 在庫を見て遷移先を変えたいのに、状態クラスから在庫が見えない
- 第7回 — 状態に文字列のような間違った値が入っても、その場ではエラーにならない
- 第8回 — 新しい状態を足すときに、VendingMachine やほかの状態クラスを触りたくない
仕上げて、パターンとして整理したい
全話一覧
- 第1回 自動販売機の「待機中」と「コイン投入済み」を、Perlのstate属性とif/elseで切り替える【if/else】
- 第2回 「払い出し中」と「売り切れ」を足したら、自動販売機のどのメソッドにも4つの分岐が並んだ【コードの臭い】
- 第3回 状態をif/elseで見分けるのをやめて、「待機中」そのものをMooのクラスにする【状態クラス】
- 第4回 状態クラスがdispenseを書き忘れたら、withの時点で止めたい【Moo::Role requires】
- 第5回 状態オブジェクトを付け替える仕事を、VendingMachineクラスに引き取らせる【Context】
- 第6回 状態クラスから在庫が見えない。メソッドにVendingMachineを渡して、遷移先を状態クラス自身に決めさせる【状態遷移】
- 第7回 stateに文字列を入れてもエラーにならない。Mooのisaで、ロールを持つオブジェクトだけを受け付ける【isa】
- 第8回 在庫が尽きたら「売り切れ」へ。VendingMachineには手を入れず、状態クラスを1つ足す【OCP】
- 第9回 4つの状態クラスをまとめて、c・s・d・qのコマンドで動かせる自動販売機に仕上げる【完成】
- 第10回 作ってきた自動販売機はStateパターンだった。Strategyパターンとは、誰が切り替えるかが違う【State】
必要なモジュール
全10回とも Moo を使います。インストールしてください。
| |
