gof-refactor-to-pattern について
目標の GoF パターンが決まっている開発者向けに、テストで確認しながら小さな手順で既存コードを移行するスキルです。各段階で動作を維持する方針を示します。
主な機能
- 01段階的なパターン移行
- 02動作を保つ変更
- 03テスト優先の指針
- 04一般的な移行手順の提示
- 05リファクタリングと機能変更の分離
活用シーン
- 01大きな型分岐を Strategy に変更
- 02条件付き拡張を Decorator オブジェクトに移行
- 03状態分岐のロジックを State に変換
- 04Singleton の反復利用を依存性注入に置換
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill gof-refactor-to-patternインストール後、Claude Code で/gof-refactor-to-patternを入力して読み込まれたか確認します。
詳細
1 行での概要
既存のコードを少しずつ目標とする GoF パターンへ近づけます。小さなコミットに分け、各ステップで振る舞いを維持し、テストを常に成功させます。
このスキルを使う場面
- ユーザーが移行先のパターンを把握しており、そこに至る手順を知りたい場合。
- コードスメルに対するパターンに沿った解決策がわかっている場合(enum に対する大きな switch → State、プリミティブなフラグの増加 → Decorator)。
- 手続き型コードやトランザクションスクリプトから、パターンを使ったオブジェクト指向設計へ移行する場合。
このスキルを使わない場面
- ユーザーがどのパターンが適切かわからない場合 —
gof-identifyに案内します。 - ユーザーがリファクタリングではなくレビューを希望する場合 —
gof-reviewに案内します。 - ユーザーが 新しい コードにパターンを使いたい場合 →
gof-implementスタイルに案内します(ここでは、直接コードを書く場合はgof-kotlin-idiomsが該当します)。
主な内容
基本方針:1 つのコミットでリファクタリングと機能変更を決して組み合わせないでください。各リファクタリングのステップでは振る舞いを変えず、テストに合格する必要があります。テストがない場合は、まず特性テストを書いてください。
よくあるリファクタリングとその手順:
型コードに対する大きな switch → Strategy。
- 変化する振る舞いを特定します(各
switchcase 内のメソッド本体)。 - 各分岐を、共通の
Strategyインターフェースを実装する独立したオブジェクトに切り出します。 - switch を、Strategy を選択する map /
whenに置き換えます。 - クライアントが Strategy の参照を直接保持するまで、検索処理をインライン化します。
バリエーションごとのサブクラス → Strategy または Composition。
- サブクラス間で変化する唯一の要素を特定します。
- その要素を Strategy インターフェースとして切り出します。
- サブクラスを 1 つのクラスに統合し、Strategy を組み合わせるようにします。
1 つのクラスに蓄積する条件付きの振る舞い → Decorator。
if (hasX)チェックに隠れた、直交する拡張要素を特定します。- 各拡張要素を、ベースをラップする Decorator クラスに切り出します。
- フラグを切り替えるのではなく、生成箇所で Decorator を組み合わせます。
複数のメソッドにあるステータス enum と switch → State。
- 状態図を明示的に描きます。
- 状態ごとに State クラスを作成します。
- 各
switchにある状態ごとの振る舞いを、対応する State に移します。 - コンテキストは現在の State に処理を委譲します。状態遷移は、
context.state = NextStateを設定して各状態が行います。
クライアントコード内の複数箇所にある new ConcreteX() の選択 → Factory。
- 選択処理を 1 つのメソッドに集約します(Simple Factory)。
- 選択がサブクラスの識別情報に依存する場合は、Factory Method に発展させます。
- 対応するファミリーをまとめて生成する必要がある場合は、Abstract Factory に発展させます。
複数のサブシステム間の直接呼び出し → Facade。
- 呼び出し元が使う共通の一連の処理を特定します。
- 高レベルの意図を表すメソッドを持つ Facade クラスを定義します。
- 呼び出し元を Facade に移行します。高度な利用者が使えるように、サブシステムには引き続きアクセスできるようにします。
あちこちにある静的な getInstance() → Dependency Injection。
- Singleton の呼び出し元を特定します。
- コンストラクター引数(またはフィールド)を追加して、依存関係を渡します。
- アプリケーションのコンポジションルートで依存関係を一度だけ設定します。
getInstance()を削除します。
判断のヒューリスティクス
- 各リファクタリングのステップは小さく、元に戻せるコミット可能なものにします。
- 各ステップの間にテストを実行します。テストがない場合は、まず特性テストを書きます(記録して再生する方式)。
- リファクタリングと機能変更を同じコミットに含めないでください。
- リファクタリングが大規模になったら、複数の PR に分割します。まずリファクタリングを行い、その後に機能を追加します。
アンチパターン
- 「今度こそ正しくやる」という名目で行う一括書き換え。
- テストなしのリファクタリング。
- 構造を変えずに、クラス名をパターン名に変えること。
- リファクタリングと機能変更を同じコミットに含めること — レビューや取り消しが難しくなります。
関連項目
- 13 種類すべてのパターンスキル(移行先)。
- 移行先がわからない場合の
gof-identify。 - リファクタリングに値するコードスメルを見つける
gof-review。 - DDD に特化した対応スキルの
ddd-refactor。
参考資料
- Head First Design Patterns(第 2 版)、第 13 章(実世界でのパターン)。
- Joshua Kerievsky 著、Refactoring to Patterns(2004 年)— 同じ考え方を本 1 冊にわたって解説しています。このスキルを補完する内容です。
コメント
Markdown 対応