codeprobe-solid について
5つのSOLID設計原則に照らしてコードを監査するコードレビュー用スキルです。責務、拡張性、置換可能性、インターフェース、依存関係の問題を特定します。
主な機能
- 015つのSOLID原則を監査
- 02責務が混在するクラスを指摘
- 03拡張と置換のパターンを確認
- 04広範または未使用のインターフェースを検出
- 05具象依存への結合を検出
活用シーン
- 01コード変更時のアーキテクチャレビュー
- 02無関係な責務を持つクラスの特定
- 03型による分岐の拡張性評価
- 04インフラへの直接依存の発見
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ machineroom-dev/ codeprobe --skill codeprobe-solidインストール後、Claude Code で/codeprobe-solidを入力して読み込まれたか確認します。
詳細
スタンドアロンモード
オーケストレーター経由ではなく直接実行された場合は、まず以下を行います。
- 出力仕様、実行モード、制約を確認するため、
../codeprobe/shared-preamble.md(この SKILL.md の場所を基準に解決します。ユーザーのプロジェクトではなく、隣接するcodeprobeスキルディレクトリを参照します)を読みます。 - プロジェクトの技術スタックに基づき、該当する参照ファイルを
../codeprobe/references/(同じ基準で解決します)から読み込みます。 - ユーザーが別のモードを指定しない限り、デフォルトで
fullモードを使用します。
SOLID 原則監査
対象範囲
このサブスキルでは、5 つの SOLID 原則への違反を検出します。
- 単一責任の原則(SRP) — クラスまたはモジュールが変更される理由は 1 つだけであるべきです。
- 開放/閉鎖の原則(OCP) — ソフトウェアの構成要素は拡張に対して開かれ、変更に対して閉じられているべきです。
- リスコフの置換原則(LSP) — サブタイプはプログラムの正しさを損なうことなく、基底型と置き換え可能でなければなりません。
- インターフェース分離の原則(ISP) — クライアントが使用しないインターフェースへの依存を強制されるべきではありません。
- 依存関係逆転の原則(DIP) — 上位モジュールは下位モジュールに依存すべきではありません。両者とも抽象に依存すべきです。
検出対象外
- 複数のフィールドを持つ単純な DTO/値オブジェクト — クラスが単一のデータ概念を表す場合、プロパティが多くても SRP 違反にはなりません。
- 依存性注入を必要としない小規模なスクリプトや CLI — 設計は規模に見合ったものであることが重要です。
- 列挙型が安定しており、選択肢が固定されている場合の switch 内での使用(例:方角、曜日)— OCP が適用されるのは、選択肢が増える可能性がある場合です。
- フレームワークの規約に従う、フレームワークが生成したクラス(例:Laravel のマイグレーション、Django の管理クラス、標準的なコールバックを使用する Rails ActiveRecord モデル)。
- セットアップ用ユーティリティをまとめたテストヘルパークラス — テスト基盤には異なる設計上の制約があります。
検出手順
SRP — 単一責任の原則
| ID Prefix | Signal | How to Detect | Severity |
|---|---|---|---|
SRP | 無関係な処理を行う public メソッドが 5 個以上あるクラス | public メソッドの数を数えます。5 個以上ある場合、単一の責任にまとまっているか、複数の関心事(例:認証+メール+データベース)にまたがっているかを確認します。異なるドメインを示唆するメソッド名の接頭辞にも注目します。 | Major |
SRP | 複数の関心事を扱う、30 LOC を超えるメソッド | 各メソッドの行数を数えます(空行とコメントは除外)。30 LOC を超える場合、抽出可能な複数の異なる処理(検証、変換、永続化、通知など)を扱っているか確認します。 | Minor |
SRP | クラス名が曖昧 | Manager、Handler、Utils、Helper、Processor、Service(ドメイン名で修飾されていない場合)という名前のクラスを検出します。これらの名前は、クラスに明確な単一の目的がないことを示唆します。例外:クラスが小さい(メソッドが 5 個未満かつ 100 LOC 未満)場合は、指摘の重大度を下げて提案とします。 | Minor |
SRP | コンストラクターが 5 個以上の依存関係を受け取る | コンストラクターパラメーターまたは注入された依存関係の数を数えます。依存関係が 5 個以上ある場合、クラスの責任が多すぎる可能性があります。依存関係が異なるドメインに属するか確認します。 | Major |
OCP — 開放/閉鎖の原則
| ID Prefix | Signal | How to Detect | Severity |
|---|---|---|---|
OCP | 型またはステータスに基づく switch/if-else の連鎖 | 型、ステータス、ロール、種別のフィールドに基づいて分岐する switch/if-else の連鎖を探します。特に、新しい選択肢を追加する際にこのブロックの変更が必要となるものに注目します。ケース数を数えます。拡張可能な型に対するケースが 3 個以上ある場合は、検出の兆候です。 | Major |
OCP | 選択肢が増える可能性があるのに拡張ポイントがない | 型に基づく switch/if-else が見つかった場合、既存コードを変更せずに新しい選択肢を追加できるインターフェース、抽象クラス、ストラテジーパターン、プラグイン機構があるか確認します。ない場合は指摘します。 | Minor |
LSP — リスコフの置換原則
| ID Prefix | シグナル | 検出方法 | 重大度 |
|---|---|---|---|
LSP | サブクラスがメソッドをオーバーライドし、意味を変更している | オーバーライドされたメソッドを確認します。サブクラスが根本的に異なる型を返す、戻り値の意味を変える、または親クラスにはない副作用を発生させていないかを調べます。メソッドのシグネチャとドキュメントコメントを比較します。 | Major |
LSP | サブクラスが親クラスで宣言されていない例外をスローする | 親クラスの throws 節やドキュメント化された契約に記載されていない例外をスローする、オーバーライドされたメソッドを探します。動的言語では、親メソッドが例外をスローしない場合に、オーバーライド内で raise/throw が使われていないかを確認します。 | Major |
LSP | サブクラスが親クラスの動作を無視する、または何もしない | 空の本体、pass、return null、return、または // not implemented コメントを含む、オーバーライドされたメソッドを探します。サブクラスが親クラスの契約を拒否しています。 | Minor |
LSP | ポリモーフィックな呼び出し後の instanceof/型チェック | ポリモーフィックに使用すべきオブジェクトに対する instanceof、is_a、typeof、type()、is のチェックを検索します。コードが基底型のメソッドを呼び出した後に具象型を確認している場合、置換可能性が損なわれていることを示しています。 | Major |
ISP — インターフェース分離の原則
| ID Prefix | シグナル | 検出方法 | 重大度 |
|---|---|---|---|
ISP | インターフェースにメソッドが8個以上ある | 各インターフェース/抽象クラス/プロトコルで宣言されているメソッド数を数えます。8個以上ある場合、インターフェースの範囲が広すぎる可能性があります。メソッドが異なるグループに分かれているか確認します。 | Minor |
ISP | クラスがインターフェースを実装しているが、メソッドが空、または例外をスローする | 1つ以上のメソッドが空、null を返す、NotImplementedError/UnsupportedOperationException をスローする、または pass/TODO コメントだけを含む、インターフェースの実装を探します。 | Major |
ISP | 1つのインターフェースに複数の無関係なメソッドグループがある | インターフェースのメソッドを分析します。読み取り操作、書き込み操作、通知メソッドなど、2つ以上の異なる責務グループに分かれていませんか。その場合、インターフェースを分割する必要があります。 | Major |
DIP — 依存関係逆転の原則
| ID Prefix | シグナル | 検出方法 | 重大度 |
|---|---|---|---|
DIP | ビジネスロジック内の new ConcreteClass() | ビジネスロジックのメソッド内で、インフラストラクチャクラスやサービスクラスを直接インスタンス化(new ClassName()、直接のコンストラクター呼び出し)していないか検索します。DTO、値オブジェクト、例外、コレクション、ファクトリー、DI コンテナーへの登録は除外します。 | Major |
DIP | 高レベルモジュールがインフラストラクチャ層から直接インポートしている | import 文を確認します。ドメイン/ビジネスロジックのモジュールが、抽象化レイヤーを介さずにデータベースドライバー、HTTP クライアント、ファイルシステムライブラリ、またはサードパーティ製 SDK から直接インポートしていませんか。import mysql、use Illuminate\Support\Facades\DB、from requests import のようなパターンを探します。 | Major |
DIP | コンストラクターインジェクションがなく、具象依存への静的メソッド呼び出しがある | インターフェースが注入されていないビジネスロジック内で、Database::query()、Cache::get()、Logger.log() のような静的メソッド呼び出しを探します。クラスが静的アクセスを通じて具象実装に強く結合しています。 | Minor |
ID Prefix と修正プロンプトの例
SRP-— 単一責任の原則への違反OCP-— オープン・クローズドの原則への違反LSP-— リスコフの置換原則への違反ISP-— インターフェース分離の原則への違反DIP-— 依存関係逆転の原則への違反
各 Prefix 内で、指摘事項に連番を付けます:SRP-001、SRP-002、OCP-001 など。
プロンプトの例を修正する
- 「
src/OrderService.phpをリファクタリングし、45〜78 行目の決済ロジックを新しいPaymentServiceクラスに切り出してください。コンストラクターを通じてPaymentServiceをOrderServiceに注入してください。calculateTotal()、applyDiscount()、processPayment()メソッドを新しいクラスに移してください。」 - 「
NotificationSenderの 30〜65 行目にある$typeによる switch 文を、Strategy パターンに置き換えてください。send(Message $message)メソッドを持つNotificationChannelインターフェースを作成してください。EmailChannel、SmsChannel、PushChannelの実装を作成してください。NotificationChannelFactoryを使って、タイプに応じた適切なチャネルを解決してください。」 - 「
UserRepository.phpの 23 行目にあるnew MySqlConnection()をコンストラクターインジェクションに置き換えてください。コンストラクターにDatabaseConnectionInterface $connectionパラメーターを追加し、具象クラスをインスタンス化する代わりにそれを使用してください。」
コメント
Markdown 対応