solid-principles
SOLID object-oriented design principles for maintainable code
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o solid-principles.zip https://jpskill.com/download/17512.zip && unzip -o solid-principles.zip && rm solid-principles.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17512.zip -OutFile "$d\solid-principles.zip"; Expand-Archive "$d\solid-principles.zip" -DestinationPath $d -Force; ri "$d\solid-principles.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
solid-principles.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
solid-principlesフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
SOLID原則
保守性、拡張性の高いソフトウェアにつながる、オブジェクト指向設計のための5つの原則です。
S - 単一責任の原則
クラスは変更される理由を一つだけ持つべきです。
// BAD - 複数の責任
class UserService {
createUser(data: UserData) { /* ... */ }
sendEmail(user: User, message: string) { /* ... */ }
generateReport(users: User[]) { /* ... */ }
validateEmail(email: string) { /* ... */ }
}
// GOOD - それぞれが単一の責任
class UserService {
constructor(
private repository: UserRepository,
private validator: UserValidator
) {}
createUser(data: UserData): User {
this.validator.validate(data);
return this.repository.save(data);
}
}
class EmailService {
send(to: string, message: string) { /* ... */ }
}
class UserReportGenerator {
generate(users: User[]): Report { /* ... */ }
}
class EmailValidator {
validate(email: string): boolean { /* ... */ }
}
適用するタイミング: クラスを「~と~」("and")で説明する場合 (UserService はユーザーを作成し、かつメールを送信し、かつ...)、分割します。
O - オープン・クローズドの原則
拡張に対して開き、修正に対して閉じる。
// BAD - 新しい型を追加するにはクラスを修正する必要がある
class PaymentProcessor {
process(payment: Payment) {
if (payment.type === 'credit') {
// クレジットカードの処理
} else if (payment.type === 'paypal') {
// PayPal の処理
} else if (payment.type === 'crypto') {
// 暗号通貨の処理 - 修正が必要になった!
}
}
}
// GOOD - 修正せずに拡張
interface PaymentMethod {
process(amount: number): Promise<Receipt>;
}
class CreditCardPayment implements PaymentMethod {
async process(amount: number): Promise<Receipt> {
// クレジットカードのロジック
}
}
class PayPalPayment implements PaymentMethod {
async process(amount: number): Promise<Receipt> {
// PayPal のロジック
}
}
// 暗号通貨の追加は既存のコードを修正しない
class CryptoPayment implements PaymentMethod {
async process(amount: number): Promise<Receipt> {
// 暗号通貨のロジック
}
}
class PaymentProcessor {
process(method: PaymentMethod, amount: number) {
return method.process(amount);
}
}
適用するタイミング: 新しい機能を追加するために、既存のテスト済みのコードを修正する必要がある場合。
L - リスコフの置換原則
サブタイプは、そのベースタイプと置換可能でなければなりません。
// BAD - Square は Rectangle の契約に違反する
class Rectangle {
constructor(protected width: number, protected height: number) {}
setWidth(width: number) { this.width = width; }
setHeight(height: number) { this.height = height; }
getArea() { return this.width * this.height; }
}
class Square extends Rectangle {
setWidth(width: number) {
this.width = width;
this.height = width; // 期待に反する!
}
setHeight(height: number) {
this.width = height;
this.height = height; // 期待に反する!
}
}
// これは壊れる:
function doubleWidth(rect: Rectangle) {
const originalHeight = rect.getArea() / rect.width;
rect.setWidth(rect.width * 2);
// Square の場合、高さも2倍になる - 予期しない!
}
// GOOD - 階層を分離する
interface Shape {
getArea(): number;
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
getArea() { return this.width * this.height; }
}
class Square implements Shape {
constructor(private side: number) {}
getArea() { return this.side * this.side; }
}
適用するタイミング: サブクラスのオーバーライドが、呼び出し元が依存する動作を変更する場合。
I - インターフェース分離の原則
クライアントは、使用しないインターフェースに依存すべきではありません。
// BAD - 太ったインターフェース
interface Worker {
work(): void;
eat(): void;
sleep(): void;
attendMeeting(): void;
writeReport(): void;
}
class Robot implements Worker {
work() { /* ... */ }
eat() { throw new Error('Robots do not eat'); } // 実装を強制される!
sleep() { throw new Error('Robots do not sleep'); }
attendMeeting() { throw new Error('Not applicable'); }
writeReport() { throw new Error('Not applicable'); }
}
// GOOD - 分離されたインターフェース
interface Workable {
work(): void;
}
interface Feedable {
eat(): void;
}
interface Sleepable {
sleep(): void;
}
interface MeetingAttendee {
attendMeeting(): void;
}
class Human implements Workable, Feedable, Sleepable, MeetingAttendee {
work() { /* ... */ }
eat() { /* ... */ }
sleep() { /* ... */ }
attendMeeting() { /* ... */ }
}
class Robot implements Workable {
work() { /* ... */ }
}
適用するタイミング: クラスが不要なメソッドを実装したり、「実装されていません」エラーをスローしたりする場合。
D - 依存性逆転の原則
具象ではなく抽象に依存する。
// BAD - 高レベルモジュールが低レベルモジュールに依存する
class MySQLDatabase {
query(sql: string) { /* ... */ }
}
class UserRepository {
private db = new MySQLDatabase(); // 強い結合!
findById(id: string) {
return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
}
}
// GOOD - 両方が抽象に依存する
interface Database {
query<T>(sql: string): Promise<T>;
}
class MySQLDatabase implements Database {
async query<T>(sql: string): Promise<T> { /* ... */ }
}
class PostgreSQLDatabase implements Database {
async query<T>(sql: string): Promise<T> { /* ... */ }
}
class UserRepository {
constructor(private db: Database) {} // 注入される!
findById(id: string) {
return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
}
}
// 実装の交換が容易
const repo = new UserRepository(new PostgreSQLDatabase());
適用するタイミング: テストが難しい場合、またはあるモジュールの変更が他のモジュールを壊す場合。
実践における SOLID
違反の認識
| 原則 | コードの臭い |
|---|---|
| SRP | クラスに無関係なメソッドが多い |
| OCP | 機能の追加に既存のコードの修正が必要 |
| LSP | サブクラスが「サポートされていません」をスローするか、異なる動作をする |
| ISP | クラスが使用しないメソッドを実装する |
(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
SOLID Principles
Five principles for object-oriented design that lead to maintainable, extensible software.
S - Single Responsibility Principle
A class should have only one reason to change.
// BAD - multiple responsibilities
class UserService {
createUser(data: UserData) { /* ... */ }
sendEmail(user: User, message: string) { /* ... */ }
generateReport(users: User[]) { /* ... */ }
validateEmail(email: string) { /* ... */ }
}
// GOOD - single responsibility each
class UserService {
constructor(
private repository: UserRepository,
private validator: UserValidator
) {}
createUser(data: UserData): User {
this.validator.validate(data);
return this.repository.save(data);
}
}
class EmailService {
send(to: string, message: string) { /* ... */ }
}
class UserReportGenerator {
generate(users: User[]): Report { /* ... */ }
}
class EmailValidator {
validate(email: string): boolean { /* ... */ }
}
When to apply: If you describe a class with "and" (UserService creates users AND sends emails AND...), split it.
O - Open/Closed Principle
Open for extension, closed for modification.
// BAD - must modify class to add new types
class PaymentProcessor {
process(payment: Payment) {
if (payment.type === 'credit') {
// process credit card
} else if (payment.type === 'paypal') {
// process PayPal
} else if (payment.type === 'crypto') {
// process crypto - had to modify!
}
}
}
// GOOD - extend without modification
interface PaymentMethod {
process(amount: number): Promise<Receipt>;
}
class CreditCardPayment implements PaymentMethod {
async process(amount: number): Promise<Receipt> {
// credit card logic
}
}
class PayPalPayment implements PaymentMethod {
async process(amount: number): Promise<Receipt> {
// PayPal logic
}
}
// Adding crypto doesn't modify existing code
class CryptoPayment implements PaymentMethod {
async process(amount: number): Promise<Receipt> {
// crypto logic
}
}
class PaymentProcessor {
process(method: PaymentMethod, amount: number) {
return method.process(amount);
}
}
When to apply: When adding new features requires modifying existing, tested code.
L - Liskov Substitution Principle
Subtypes must be substitutable for their base types.
// BAD - Square violates Rectangle contract
class Rectangle {
constructor(protected width: number, protected height: number) {}
setWidth(width: number) { this.width = width; }
setHeight(height: number) { this.height = height; }
getArea() { return this.width * this.height; }
}
class Square extends Rectangle {
setWidth(width: number) {
this.width = width;
this.height = width; // Violates expectation!
}
setHeight(height: number) {
this.width = height;
this.height = height; // Violates expectation!
}
}
// This breaks:
function doubleWidth(rect: Rectangle) {
const originalHeight = rect.getArea() / rect.width;
rect.setWidth(rect.width * 2);
// For Square, height also doubled - unexpected!
}
// GOOD - separate hierarchies
interface Shape {
getArea(): number;
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
getArea() { return this.width * this.height; }
}
class Square implements Shape {
constructor(private side: number) {}
getArea() { return this.side * this.side; }
}
When to apply: If subclass overrides change behavior that callers depend on.
I - Interface Segregation Principle
Clients should not depend on interfaces they don't use.
// BAD - fat interface
interface Worker {
work(): void;
eat(): void;
sleep(): void;
attendMeeting(): void;
writeReport(): void;
}
class Robot implements Worker {
work() { /* ... */ }
eat() { throw new Error('Robots do not eat'); } // Forced to implement!
sleep() { throw new Error('Robots do not sleep'); }
attendMeeting() { throw new Error('Not applicable'); }
writeReport() { throw new Error('Not applicable'); }
}
// GOOD - segregated interfaces
interface Workable {
work(): void;
}
interface Feedable {
eat(): void;
}
interface Sleepable {
sleep(): void;
}
interface MeetingAttendee {
attendMeeting(): void;
}
class Human implements Workable, Feedable, Sleepable, MeetingAttendee {
work() { /* ... */ }
eat() { /* ... */ }
sleep() { /* ... */ }
attendMeeting() { /* ... */ }
}
class Robot implements Workable {
work() { /* ... */ }
}
When to apply: When classes implement methods they don't need, or throw "not implemented" errors.
D - Dependency Inversion Principle
Depend on abstractions, not concretions.
// BAD - high-level depends on low-level
class MySQLDatabase {
query(sql: string) { /* ... */ }
}
class UserRepository {
private db = new MySQLDatabase(); // Tight coupling!
findById(id: string) {
return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
}
}
// GOOD - both depend on abstraction
interface Database {
query<T>(sql: string): Promise<T>;
}
class MySQLDatabase implements Database {
async query<T>(sql: string): Promise<T> { /* ... */ }
}
class PostgreSQLDatabase implements Database {
async query<T>(sql: string): Promise<T> { /* ... */ }
}
class UserRepository {
constructor(private db: Database) {} // Injected!
findById(id: string) {
return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
}
}
// Easy to swap implementations
const repo = new UserRepository(new PostgreSQLDatabase());
When to apply: When testing is hard, or changing one module breaks others.
SOLID in Practice
Recognizing Violations
| Principle | Code Smell |
|---|---|
| SRP | Class has many unrelated methods |
| OCP | Adding feature requires modifying existing code |
| LSP | Subclass throws "not supported" or behaves differently |
| ISP | Class implements methods it doesn't use |
| DIP | new keyword scattered throughout business logic |
Applying SOLID
- Start simple - Don't over-engineer from day one
- Refactor when needed - Apply when you feel the pain
- Use dependency injection - Makes DIP natural
- Prefer composition - Over inheritance (helps LSP)
- Write small interfaces - Easier than splitting later
Balance
SOLID is a guide, not law. Over-applying creates:
- Too many tiny classes
- Indirection that's hard to follow
- Abstractions nobody needs yet
Apply SOLID when:
- Code is hard to test
- Changes ripple through the codebase
- Similar changes needed in multiple places
- You're adding the 3rd variation of something
Checklist
- [ ] Does each class have a single, clear purpose?
- [ ] Can I add features without modifying existing code?
- [ ] Can subclasses replace parent classes safely?
- [ ] Are interfaces focused and minimal?
- [ ] Are dependencies injected, not created internally?