design-patterns について
コード構造をデザインパターンの観点から検討する開発者向けのエージェントスキルです。実際の課題とシンプルな設計原則を先に確認し、複雑さを加えるコストも説明します。
主な機能
- 01症状から候補パターンを提示
- 02原則とシンプルな案を先に確認
- 03一般的なパターンと適用場面を収録
- 04推奨時に導入コストを明示
活用シーン
- 01StrategyやDecoratorの適用を検討
- 02条件分岐したオブジェクト生成を整理
- 03構造上の提案とトレードオフを説明
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ robsonrung/ rar-skills --skill design-patternsインストール後、Claude Code で/design-patternsを入力して読み込まれたか確認します。
同梱ファイルreferences/patterns.md
詳細
デザインパターン
_Fredman & Robson 著『Head First Design Patterns』(第2版)から導き出した、パターン認識の視点です。目標はパターンを適用することではありません。パターンが自然に当てはまる場面と、当てはまらない場面を見極め、実際の問題を解決する最もシンプルな方法を選ぶことです。
まずはガードレールを意識する
この本で最も重要な教訓が語られるのは、最初の章ではなく最終章です。ほとんどのコードでは、名前の付いたパターンを使うべきではありません。
- シンプルに保つ(KISS)。 設計では、問題をできるだけシンプルな方法で解決しましょう。目指すのはシンプルさであり、「ここでパターンを適用するにはどうすればよいか?」ではありません。単純な関数や
map、早期リターンが適切な場合も多く、ほかの開発者もそのシンプルさを高く評価するでしょう。 - パターンは無料ではありません。 それぞれのパターンによって、クラスや間接性、レイヤーが増えます。読む量や追跡する量が増え、場合によっては効率も下がります。そのコストに見合うのは、設計が実際に必要とする柔軟性を得られる場合だけです。
- 必要性が生じてからパターンを導入しましょう。先回りして導入するのではありません。 よりシンプルな解決策でうまくいく可能性があるなら、まずは試してみましょう。パターンを選ぶのは、今まさに直面している実際の問題、たとえば同じ箇所で繰り返し変更が発生することや、何度も苦労する拡張ポイントを解決できると確信したときです。仮定上の将来の問題に備えるためではありません。
- (後述する)設計原則に立ち返れば、名前の付いたパターンを使わずに問題が解消することもよくあります。 そうなったら、無理にパターンを使う必要はありません。
ですから、「ここではどのパターンを使うべきですか?」への正直な答えは、ときに**「何も使いません。こちらのシンプルな構造にしましょう」**です。以下の「出力形式」セクションにはこの考え方と、推奨事項には必ずコストを明記するというルールが反映されています。
この視点の使い方
コードの構造が問題になったときは、次の順序で考えましょう。
- 何が問題なのかを明確にします。 実際に難しいのは何でしょうか? 多くの場合、変化し続ける振る舞い、似たクラス間の重複、拡張しにくい硬直した構造、密結合、条件分岐が散在するオブジェクト生成のいずれかです。
- 設計原則を確認します。 これらはパターンの基盤となるものです。設計原則を直接適用するだけで解決することも多く、その場合は名前の付いたパターンは必要ありません。
- その後で初めて、症状の表を使ってパターンを当てはめます。 2つのパターンが当てはまるなら、仕組みが少ないほうを選びましょう。
- 共通の語彙を使います。 パターンが本当に当てはまるなら、名前を付けましょう。「これはストラテジーです」「これはデコレーターです」のように伝えます。共通の名前を使えば、設計全体(意図、構造、トレードオフ)を一言で次の開発者に伝えられます。この伝達価値こそ、パターンを知っておく価値の半分を占めています。
設計原則(まずはこちらを確認)
これらは、個々のパターンよりも広く役立つ原則です。優れたリファクタリングの大半は、次のいずれかを直接適用したものです。
- 変化するものをカプセル化します。 変化する部分を特定し、変わらない部分から分離して、変更の影響を局所化します。
- 実装ではなくインターフェースに対してプログラミングします。 具象クラスではなく機能(型/抽象)に依存することで、実装を切り替えられるようにします。
- 継承よりコンポジションを優先します。 「HAS-Aは、多くの場合IS-Aより適切です」。振る舞いを組み合わせる方法は、クラス階層に固定する方法より柔軟です。
- 相互作用するオブジェクト間の結合を疎にすることを目指します。 互いについて知ることは、できるだけ少なくすべきです。
- オープン・クローズドの原則: クラスは拡張に対して開き、変更に対して閉じているべきです。テスト済みのコードを編集せずに、新しい振る舞いを追加できるようにします。
- 依存関係逆転の原則: 具象クラスではなく抽象に依存します。高水準のコードが低水準の詳細に左右されないようにします。
- 最小知識の原則(デメテルの法則): 直接の知り合いとのみやり取りします。
a.getB().getC().doThing()のような長い連鎖は避けましょう。 - ハリウッドの原則: 「こちらから連絡するので、お待ちください」。逆ではなく、高水準のコンポーネントが低水準のコンポーネントを呼び出すタイミングを制御します(制御の反転)。
- 単一責任の原則: クラスが変更される理由は1つだけであるべきです。クラスごとに責務は1つです。
症状 → パターン対応表
_挙げられた課題_に合う候補を選んでください。これは出発点であり、結論ではありません。導入を決める前に、references/patterns.md のパターンの項目(「やりすぎになる場合」も記載されています)を読んでください。
| 困っていること | 候補となるパターン |
|---|---|
交換可能な振る舞いを選択する、肥大化したif/else/switch | Strategy(呼び出し元/設定で振る舞いを選択)または State(オブジェクト自身の状態に応じて振る舞いが決まり、状態遷移がある) |
| 実行時に振る舞いを変更する必要がある、または任意の振る舞いの組み合わせが多数ある | Strategy。積み重ね可能な追加機能には Decorator |
| あるオブジェクトの変更を多数のオブジェクトに通知する必要がある。コールバックを手作業で配線している | Observer |
| サブクラスが際限なく増えるのを避けつつ、オブジェクトに責務をラップ/追加したい | Decorator |
new ConcreteThing() がコード中に散在している。生成ロジックが条件分岐を含み、重複している | Factory Method(単一の製品)、Abstract Factory(関連する製品ファミリー)、または単なる生成関数 |
| 共有インスタンスが厳密に1つ必要/調整を一元化したい | Singleton(ただし、通常はモジュールレベルの値やDIのほうがシンプルでテストしやすいです) |
| _アクション_をパラメーター化、キューイング、ログ記録、または取り消ししたい | Command |
| 互換性のない2つのインターフェースを連携させる必要がある | Adapter |
| 複雑なサブシステムに、シンプルな入口を1つ設けたい | Facade |
| 複数のメソッドで基本構造は共通だが、いくつかの手順が異なる | Template Method(継承)または変化する手順を渡す(Strategy方式) |
| 内部構造を公開せずにコレクションを走査したい | Iterator(多くの言語では標準で使えます) |
| クライアントが葉と枝を同じように扱う必要がある、全体と部分からなるツリー | Composite |
| 遅延読み込み、リモートアクセス、権限チェック、キャッシュなど、オブジェクトへのアクセスを制御したい | Proxy |
| 抽象化と実装がそれぞれ独立して変化し、組み合わせが増えている | Bridge |
| 複雑なオブジェクトを段階的に構築したい/任意のフィールドが多い | Builder |
| リクエストを、処理できるものに行き着くまでチェーンに沿って渡したい | Chain of Responsibility |
| クラスを編集せずに、固定されたオブジェクト構造へ新しい操作を追加したい | Visitor |
| オブジェクトの状態を保存して復元したい(取り消し/スナップショット) | Memento |
| 多数のオブジェクトが多対多で通信しており、調整が複雑になっている | Mediator |
| 類似した細粒度オブジェクトが大量にあり、メモリを圧迫している | Flyweight |
| 設定済みのインスタンスをコピーして、新しいオブジェクトを作りたい | Prototype |
| 小規模な言語/文法で記述された文を解釈したい | Interpreter |
| データ、プレゼンテーション、入力処理が絡み合っている対話型アプリ | Compound / MVC(Observer + Strategy + Composite) |
各パターンの一文での定義、パターンを示唆するコードの兆候、構造の概略、関連パターン、やりすぎになる場合を含む一覧は、references/patterns.md を読んでください。状況に関係する項目だけを読めば十分です。特定のパターンにすぐ移動できる構成になっています。
design-gate のもとでは、呼び出し元が定める標準のゲート結果を使用してください。これにより、単独の出力形式や編集手順は不要になります。レンズ固有の結論は、安定した指摘IDを付けて、指摘またはreview_focusに記載してください。面接では、既存の意思決定記録に判断フィールドを追加してください。
出力形式
構造について助言する場合は、誠実で実行可能な提案になるよう、次の形式をおすすめします。
- 課題: 実際に変更しにくいものは何か、一文で説明します。
- 最もシンプルな選択肢: 実現可能な最も簡素な方法(パターンを使わない場合もよくあります)を示し、それで十分かどうかを述べます。
- パターン(必要な場合): 共通の用語で名前を挙げ、このコードにどう適用するかを概説します。
- コスト: 追加される間接化やクラス、およびここでそれだけの価値がある理由を説明します。コストを説明できない場合は、もう一度よく検討してください。
「パターンは不要です。こちらが3行の実装です」という提案は、逃げではなく成功です。