solid について
solidは、ソフトウェアの設計、リファクタリング、レビューで参照するSOLIDの5原則を扱う開発スキルです。検出用の質問、各規模での適用方法、よくあるアンチパターンをまとめています。
主な機能
- 01SRP、OCP、LSP、ISP、DIPの解説
- 02クラスとモジュールのチェック項目
- 03関数からサービスまでの適用例
- 04アンチパターンと改善の方向性
- 05インターフェースと依存関係の設計指針
活用シーン
- 01新しいクラス、モジュール、インターフェースの設計
- 02責務が多すぎるコードのリファクタリング
- 03PRのアーキテクチャ上の問題のレビュー
- 04大規模クラスの分割やインターフェースの整理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill solidインストール後、Claude Code で/solidを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlreferences/PRINCIPLES.md
詳細
SOLID の原則
理解しやすく、拡張しやすく、保守しやすいソフトウェアを構築するための 5 つの原則です。結合度を下げ、凝集度を高め、コードをテスト可能にします。
適用する場面
次のような場合に、これらの原則を参考にしてください。
- 新しいクラス、モジュール、インターフェースを設計するとき
- 責務が多すぎるコードをリファクタリングするとき
- アーキテクチャ上の懸念について PR をレビューするとき
- 巨大なオブジェクトや肥大化したインターフェースを分割するとき
- モジュールの境界をどこに設けるか決めるとき
- コードをよりテストしやすくするとき
クイックリファレンス
| 原則 | 一言で言うと | 危険信号 |
|---|---|---|
| SRP | 変更理由は 1 つ | 「このクラスは X も Y も Z も担当する」 |
| OCP | 変更せずに追加する | 型ごとの if/else や switch の連鎖が増えていく |
| LSP | サブタイプは置換可能 | 呼び出し側のコードで型チェックや特別扱いが必要 |
| ISP | 小さく、焦点を絞ったインターフェース | 空のメソッド実装、または throw new Error("Not implemented") |
| DIP | 抽象に依存する | ビジネスロジック内の new ConcreteClass() |
詳しい説明と TypeScript の例については、references/PRINCIPLES.md を参照してください。
検出チェックリスト
すべてのクラスとモジュールについて、次の質問をしてください。
| 質問 | 違反している原則 |
|---|---|
| このクラスには、変更する理由が複数ありますか? | SRP |
| 新しいバリエーションを追加するために、既存のコードを変更する必要がありますか? | OCP |
| 呼び出し側のコードで、サブタイプの型チェックや特別扱いが必要ですか? | LSP |
| 実装者は、使わないメソッドのスタブ実装を強いられていますか? | ISP |
| 高水準のロジックがインフラストラクチャを直接インスタンス化していますか? | DIP |
さまざまな規模で SOLID を適用する
| 規模 | SRP | OCP | LSP | ISP | DIP |
|---|---|---|---|---|---|
| 関数 | 1 つのことを行う | — | — | — | パラメーターとして抽象を受け取る |
| クラス | 変更理由は 1 つ | 合成によって拡張する | サブタイプは契約を守る | 使用するものだけを実装する | コンストラクターインジェクション |
| モジュール | 1 つの境界づけられたコンテキスト | プラグインアーキテクチャ | 実装を相互に置き換え可能にする | 薄い公開 API | 内側に依存する |
| サービス | 1 つのドメイン | 新機能 = 新しいサービス | API 契約の安定性 | 最小限の API サーフェス | 境界に抽象を置く |
原則間の関係
- SRP + ISP:責務を分割すると、インターフェースも分割することが多くなります
- OCP + DIP:抽象に依存することで、変更せずに拡張できるようになります
- LSP + OCP:サブタイプを置換できれば、新しいサブタイプを追加して振る舞いを拡張できます
- DIP + ISP:小さく焦点を絞ったインターフェースによって、依存関係逆転を実用的に適用できます
よくあるアンチパターン
| アンチパターン | 違反している原則 | 対処法 |
|---|---|---|
| あらゆることを行う巨大なクラス | SRP | 焦点を絞ったクラスを切り出す |
コードベース全体で型に応じて switch する | OCP, LSP | ポリモーフィズムに置き換える |
| 「サポートされていない」と例外を投げるサブクラス | LSP, ISP | 階層を再設計し、インターフェースを分割する |
| 20 個のメソッドを持つ肥大化したインターフェース | ISP | 役割ごとのインターフェースに分割する |
| DB ドライバーをインポートするビジネスロジック | DIP | リポジトリーインターフェースを注入する |
| 自身の依存関係を生成するサービス | DIP | コンストラクターインジェクション |
ベストプラクティス
すること
- SRP から始めます。ほかのすべての原則の基盤です
- コンポーネント間の境界を定義するためにインターフェースを使用します
- 実際の問題から違反が明らかになってから、対処します
- 振る舞いを拡張する場合は、継承より合成を優先します
- インターフェースを小さく、役割に特化したものに保ちます
- コンストラクターを通じて依存関係を注入します
しないこと
- 些細なコードに SOLID を盲目的に適用しません(5 行のユーティリティにインターフェースは不要です)
- 少なくとも 2 つの実装ができる前に抽象を作成しません
- SRP を「メソッドが 1 つ」と混同しません。重要なのはサイズではなく、変更する理由です
- 同じ階層に属すべきでないクラスに、リスコフの置換可能性への準拠を強制しません
- まとまりのあるグループが適切な場合に、インターフェースを単一メソッドの断片に過度に分割しません
コメント
Markdown 対応