GoF(Gang of Four)のデザインパターン23種を、困ったコードを直す話として1話ずつ書いた全23話の連載です。各話では、持ち込まれたコードの困りごとを見立て、パターンを使って直すまでを物語仕立てでたどります。
どの話からでも読めます。実装は Perl ですが、コードを書ける必要はありません。直す前と後のコードを見比べて、何が変わったかを追えれば十分です。
このページは目次を兼ねた案内板です。初めてなら「まず読むなら」の3本から、手元に困っているコードがあるなら「困りごとから探す」から、パターン名で引きたいなら「GoF の分類で探す」から選んでください。
まず読むなら
- Factory Method — 種類が増えるたびに伸びる if-elsif を片付ける回。多くの現場で最初に出会う困りごとで、ほかのパターンを読む足場になる
- Builder — テストデータのコピペが原因で、テストは通るのに本番が落ちる回。設計の話を抜きにしても、明日のテストコードにそのまま使える
- State — 真偽値フラグを重ねて、ありえない状態に入り込む回。フラグが3本を超えたコードを抱えているなら、いちばん効く
困りごとから探す
条件分岐が伸び続けて、足すたびにどこかが壊れる
- Factory Method — 連携先ごとに分かれた if-elsif
- Bridge — 部署×出力形式で掛け算に増える分岐
- Chain of Responsibility — アラート判定の長い if-elsif
- Visitor — 複数の関数に散らばった ref() の型判定
- State — フラグの組み合わせで分岐する状態管理
同じコードを何か所も直している
- Builder — テストごとにコピペしたテストデータ
- Prototype — 種類ごとに全項目を書いたテンプレート
- Template Method — 流れが同じ処理を丸ごとコピーしたモジュール
1つ足すと、関係ないはずの所が壊れる
- Abstract Factory — 出力形式を足したら既存の形式が崩れた
- Strategy — アルゴリズムを足したら別の結果が狂った
- Mediator — モジュール同士が直接呼び合っている
- Decorator — 機能を足すたびに本体を書き換えている
- Composite — 階層を1段深くしたら集計が壊れた
重い、メモリが尽きる、相手に負荷をかける
- Singleton — 重い初期化があちこちで何度も走る
- Flyweight — 同じデータを件数分コピーしている
- Proxy — 同じデータを何度も取り直している
- Iterator — 全件を一度にメモリへ載せている
- Observer — 変化を知るために問い合わせを繰り返している
古いコードや外の仕組みに合わせたい
変更を元に戻せない、危ない動かし方をしている
- Command — 途中で失敗したデプロイを手で戻している
- Memento — 上書きした設定を前の状態に戻せない
- Interpreter — 文字列のルールを eval で実行している
GoF の分類で探す
生成に関するパターン (Creational Patterns)
- 設定読み込みが毎回走ってバッチと値がずれる。インスタンスを1つにまとめる【Singleton】
- 連携先を1つ足すと別の連携が壊れる。生成を1か所に集めて切り離す【Factory Method】
- 出力形式を1つ足したら既存のPDFとHTMLまで崩れた。部品をセットで生成する【Abstract Factory】
- テストデータのコピペで修正漏れ、テストは通るのに本番で落ちる。差分だけ指定して防ぐ【Builder】
- 項目を1つ足すだけで17か所の手直し、漏れた所が壊れる。原型を複製して作る【Prototype】
構造に関するパターン (Structural Patterns)
- 20年物のレガシーをJSONで呼びたい、でも中身は1行も触れない。変換役を挟む【Adapter】
- スマートホームの機器を順番どおり呼ばないと誤作動する。手順を窓口1つにまとめる【Facade】
- 部署×出力形式でif分岐が掛け算で増える。2つの軸を分けて組み合わせる【Bridge】
- 機能追加のたびに200行の承認フローを書き換える。機能を外から重ねて足す【Decorator】
- サブレシピを入れたら材料の集計が壊れた。入れ子のツリーを同じ操作で扱う【Composite】
- 注文のたびに同じメニュー情報を丸ごと複製、金曜の夜に固まる。共有して減らす【Flyweight】
- クエリ1本は速いのにDBのCPUが張り付く。重複した取得を代理役でまとめる【Proxy】
振る舞いに関するパターン (Behavioral Patterns)
- 予想ロジックを1つ足したら、別のロジックの的中率が70%→23%に。独立させる【Strategy】
- フッター1つ直すのに10ファイル、コピーした所で事故。共通の流れを親にまとめる【Template Method】
- 更新を見張るポーリングが相手のサーバーを落としかける。変化を通知で受け取る【Observer】
- APIの全件取得でメモリが尽きてOOM Killerに落とされる。1件ずつ取り出す【Iterator】
- デプロイが途中で失敗すると手で戻すしかない。操作ごとに取り消しを持たせる【Command】
- アラートを1つ足すたびに別のアラートが誤爆する。判定を担当ごとの鎖に分ける【Chain of Responsibility】
- フラグの組み合わせで、ありえない状態に入り込む。状態ごとにクラスを分ける【State】
- 機能を1つ足すだけで全モジュールを修正する。やり取りを仲介役に集める【Mediator】
- 設定を上書きして、うまくいった時の値に戻せない。時点ごとに保存して戻す【Memento】
- 種類を1つ足すと、ref()の分岐を全関数に書き足す羽目に。操作を外に出す【Visitor】
- evalでルールを動かしたら監査でコードインジェクション判定。文法をオブジェクトで組む【Interpreter】
