Featured image of post 週次レポートを足したら create_report が if/else だらけになった。種別が増えるほど修正が増える問題【開放閉鎖原則】

週次レポートを足したら create_report が if/else だらけになった。種別が増えるほど修正が増える問題【開放閉鎖原則】

週次レポートを追加するために、create_report に type 引数を渡して if/else で月次と週次を切り替えます。動きはしますが、日次や四半期を足すたびに ReportGenerator を書き換えることになり、開放閉鎖原則に反する形になる理由を確かめます。

Featured image of post 審査条件を1つ足すたびに既存コードを触りたくない。審査をクラスに分けて鎖でつなぐ【Chain of Responsibility】

審査条件を1つ足すたびに既存コードを触りたくない。審査をクラスに分けて鎖でつなぐ【Chain of Responsibility】

金額上限・有効期限・ブラックリストの審査を、PaymentChecker を継承した別々の Moo クラスに分け、next_handler でつないで鎖にします。審査の順序は組み立てるときに決まり、残高確認の BalanceChecker を足すのに既存のチェッカーは書き換えません。最後に Chain of Responsibility パターンと名前を付けます。

Featured image of post 審査条件を5つに増やしたら、if文のネストが5段になった。早期リターンで直しても残る問題【コードの臭い】

審査条件を5つに増やしたら、if文のネストが5段になった。早期リターンで直しても残る問題【コードの臭い】

決済審査にブラックリスト、利用枠の残高確認、10分以内に3回以上の決済を拒否する不正利用検知を足すと、if-else のネストが5段になります。早期リターンで平らに書き直しても、1つの関数に5つの責任が残り、審査条件を1つだけ試すテストも書きにくいままです。この形を整理します。

Featured image of post if/elseで状態を切り替えていたPerlの自動販売機を、状態ごとのMooクラスに分けてStateパターンにたどり着く全10回【目次】

if/elseで状態を切り替えていたPerlの自動販売機を、状態ごとのMooクラスに分けてStateパターンにたどり着く全10回【目次】

Perl で自動販売機の状態を if/else で管理するところから始め、状態が増えて分岐が膨らんだところで、状態ごとのクラスと Moo::Role、状態を抱える VendingMachine(Context)に組み直し、最後に State パターンだと名前を付ける全10回の連載の目次です。どこから読むか、困りごとからも選べます。

Featured image of post 作ってきた自動販売機はStateパターンだった。Strategyパターンとは、誰が切り替えるかが違う【State】

作ってきた自動販売機はStateパターンだった。Strategyパターンとは、誰が切り替えるかが違う【State】

第9回までに作った VendingMachineState ロール・4つの状態クラス・VendingMachine を、GoF の State パターンの State・ConcreteState・Context に当てはめます。同じく振る舞いを差し替える Strategy パターンとは、切り替えを外のクライアントが決めるか状態自身が決めるか、状態遷移があるかで分かれることを、データエクスポーターのコードと並べて確かめます。

Featured image of post 4つの状態クラスをまとめて、c・s・d・qのコマンドで動かせる自動販売機に仕上げる【完成】

4つの状態クラスをまとめて、c・s・d・qのコマンドで動かせる自動販売機に仕上げる【完成】

WaitingState・CoinInsertedState・DispensingState・SoldOutState の4つの状態クラスと VendingMachine を1本のスクリプトにまとめ、標準入力から c(コイン投入)・s(商品選択)・d(払い出し)・q(終了)を受け付ける対話的な CLI を付けて完成させます。状態遷移図とクラスの一覧で、第1回からの設計を振り返ります。

Featured image of post 在庫が尽きたら「売り切れ」へ。VendingMachineには手を入れず、状態クラスを1つ足す【OCP】

在庫が尽きたら「売り切れ」へ。VendingMachineには手を入れず、状態クラスを1つ足す【OCP】

在庫が0になったら移る売り切れ状態 SoldOutState を追加します。新しいクラスを1つ作り、DispensingState の dispense に遷移先を1か所足すだけで、VendingMachine(Context)やロール、ほかの状態クラスは変えずに済む。この足し方を、開放閉鎖原則(OCP)の形として確かめます。

Featured image of post stateに文字列を入れてもエラーにならない。Mooのisaで、ロールを持つオブジェクトだけを受け付ける【isa】

stateに文字列を入れてもエラーにならない。Mooのisaで、ロールを持つオブジェクトだけを受け付ける【isa】

VendingMachine の state には文字列でも入ってしまい、エラーになるのは次に insert_coin を呼んだときになる。Moo の has に isa のコードリファレンスを書き、値が VendingMachineState ロールを持たなければ die するようにして、set_state を呼んだ時点で間違いに気づけるようにします。

Featured image of post 状態クラスから在庫が見えない。メソッドにVendingMachineを渡して、遷移先を状態クラス自身に決めさせる【状態遷移】

状態クラスから在庫が見えない。メソッドにVendingMachineを渡して、遷移先を状態クラス自身に決めさせる【状態遷移】

次の状態を返すだけの状態クラスでは、在庫を見て遷移先を変えられない。各メソッドの引数に Context(VendingMachine)を渡し、状態クラスが $context->stock で在庫を確かめ、$context->set_state で自分から次の状態に切り替える形にします。在庫2個で3回買い、3回目は売り切れでコインが返されるのを確かめます。