コードの変更容易性・可読性・保守性を保つ基本原則。特定アーキテクチャに依存せず全プロジェクトで適用。
| 対象 | |
|---|---|
| ✅ | 全プロジェクト — 適用しない理由はない |
| ✅ | コードレビューの判断基準として |
| ⚠️ | 過剰適用(3行のスクリプトにSOLIDを持ち込む等) |
# ❌ 悪い例: 1関数・多重責務 + DRY違反
def process_order_send_email_update_stock(order):
total = sum(item["price"] * item["qty"] for item in order["items"])
import smtplib
smtp = smtplib.SMTP("smtp.example.com")
smtp.sendmail("shop@example.com", order["email"], f"合計: {total}円")
for item in order["items"]:
stock[item["id"]] -= item["qty"]
# ✅ 良い例: 責務を分離(SRP + DIP)
from typing import Protocol
class EmailSender(Protocol):
def send(self, to: str, body: str) -> None: ...
class StockRepository(Protocol):
def decrement(self, item_id: str, qty: int) -> None: ...
def calculate_total(items: list[OrderItem]) -> float:
return sum(item.price * item.qty for item in items)
def process_order(items, email: EmailSender, stock: StockRepository):
total = calculate_total(items)
for item in items:
stock.decrement(item.id, item.qty)
// ❌ 悪い例: 新しい決済方法を追加するたびに書き換える(OCP違反)
function processPayment(method: string, amount: number) {
if (method === "credit") { /* クレジットカード処理 */ }
else if (method === "bank") { /* 銀行振込処理 */ }
// 新しい方法を追加するにはここを変更 → OCP違反
}
// ✅ 良い例: 拡張には開き、修正には閉じる(OCP)
interface PaymentProcessor {
process(amount: number): Promise;
}
class CreditCardProcessor implements PaymentProcessor { /* ... */ }
class BankTransferProcessor implements PaymentProcessor { /* ... */ }
class CheckoutService {
constructor(private processor: PaymentProcessor) {} // DIP
async checkout(amount: number) {
await this.processor.process(amount);
}
}
## 設計原則
- DRY: 同じロジックを2箇所に書かない。共通関数・クラスに抽出
- KISS: 複雑な解決策よりシンプルな方を選ぶ
- YAGNI: 今必要ない機能を先回りして実装しない
- SRP: 1つの関数・クラスは1つの責務だけ持つ
- DIP: ビジネスロジックから外部API・DBを直接呼ばない(Protocol/Interfaceを挟む)