メインコンテンツまでスキップ

第8章:SRPの感覚「変更理由は1つ」ってどういうこと?🎯✨

この章は、SRP(単一責務の原則)のいちばん大事な感覚をつかむ回だよ〜😊💡 結論から言うと、SRPはこう👇

  • “メソッドが1つ”じゃない
  • “クラスが小さい”だけでもない
  • 「変更理由(=変わる理由)」が1つってこと!🎯

SRPはだいたい「クラスは1つの理由でしか変更されるべきじゃない」って表現されるよ📌 (blog.cleancoder.com) さらに最近の説明だと「1つのアクター(利害関係者のグループ)に対して責務を持つ」って言い方もよく使われるよ👥✨ (ウィキペディア)


8.1 この章のゴール🎓💖

この章を終えると、こうなれるよ👇

  • ✅ **「責務=変更理由」**で説明できるようになる🗣️✨
  • ✅ 1つのクラスに複数の変更理由が混ざってるのを見つけられる🔎
  • ✅ いきなり分割せずに、まず**“分けるべき境界の候補”**を言語化できる🧠💡

(分割テク自体は次章以降でガッツリやるよ〜🧹✨)


8.2 「変更理由」ってなに?🧩💭

変更理由カード

🔥 変更理由=「誰の都合で変わる?」

SRPの“理由”は、だいたいこれ👇

  • 🧑‍💼 経理「請求書の税計算を変えて」
  • 🛍️ 営業/マーケ「注文メールの文章変えて」
  • 🧑‍🔧 運用「配送会社を切り替えるよ」
  • 🔐 セキュリティ「ログの出し方を変えて」
  • 🧑‍⚖️ 法務「規約に合わせて表示を変えて」

この「誰の要望で変わる?」が違うのに、同じクラスに全部入ってると… 💥「え、税計算を直しただけなのにメールも壊れた」みたいな地獄が起きる😇🔥


8.3 SRPのよくある誤解ランキング😵‍💫➡️😌

❌ 誤解1:「クラスは小さくしろ!短ければOK!」

短くても、別の変更理由が混ざってたらアウトだよ〜😇

❌ 誤解2:「1クラス1メソッドにすればSRP!」

それはただの“細切れ”🍣 変更理由が同じなら、まとめてOKなことも多いよ🙆‍♀️✨

❌ 誤解3:「責務=機能1個」

SRPの責務は「機能の数」より **“変化の単位”**が本体だよ🎯 (blog.cleancoder.com)


8.4 体験してみよう:SRP違反クラスを“変更理由”で読む👀📌

ここでは、ミニECっぽい「注文確定」処理の“ぐちゃぐちゃ版”を例にするね🛒💥 (わざと密集させるよ😈✨)

public class OrderService
{
public async Task PlaceOrderAsync(OrderRequest request)
{
// ①入力チェック(仕様変更されがち)
if (request.Items.Count == 0) throw new ArgumentException("No items");
if (request.ShippingAddress is null) throw new ArgumentException("No address");

// ②割引・税計算(経理・販促で変わりがち)
decimal subtotal = request.Items.Sum(i => i.UnitPrice * i.Quantity);
decimal discount = request.CouponCode == "WELCOME10" ? subtotal * 0.10m : 0m;
decimal tax = (subtotal - discount) * 0.10m;
decimal total = subtotal - discount + tax;

// ③支払い(決済会社変更で変わりがち)
var paymentOk = await FakePaymentGateway.ChargeAsync(request.CardToken, total);
if (!paymentOk) throw new InvalidOperationException("Payment failed");

// ④保存(DB都合で変わりがち)
FakeDb.Orders.Add(new Order { Total = total, CreatedAt = DateTimeOffset.Now });

// ⑤メール送信(文言・デザイン変更で変わりがち)
string body = $"Thanks! Total: {total:0.00}";
FakeEmail.Send(request.Email, "Order Confirmed", body);

// ⑥ログ(運用・監査で変わりがち)
Console.WriteLine($"Order placed: {request.Email} total={total:0.00}");
}
}

✅ このクラス、変更理由が何個ある?🎯

ざっくりでも、もう5〜6個あるよね😇💦

  • ✅ 入力チェック(ルールが増える)
  • ✅ 割引(キャンペーンが増える)
  • ✅ 税(税率・端数処理が変わる)
  • ✅ 決済(決済会社やAPIが変わる)
  • ✅ DB(保存方式が変わる)
  • ✅ メール(テンプレ・文言が変わる)
  • ✅ ログ(監査要件が変わる)

つまりこの OrderService は、いろんな部署の事情が全部のっかってる状態👥💥 これが「変更が怖いコード」の代表例だよ〜😵‍💫


8.5 SRPを見抜くコツ:変更理由の“カード分け”🃏✨

ここからがSRPの本番!🎉 やることはシンプル👇

Step 1:変更要求を“カード”として書き出す📝💗

例:

  • 「クーポン条件を増やして」
  • 「税率を変えて」
  • 「決済をStripeに変えて」
  • 「注文メールをHTMLにして」
  • 「ログに注文ID入れて」

Step 2:“同じ理由で一緒に変わるもの”をグループ化🧺✨

  • クーポン条件と割引計算 → 同じグループになりがち🎫
  • メール文言とメール件名 → 同じグループになりがち📩
  • DB保存とDBスキーマ → 同じグループになりがち🗄️

Step 3:グループに「名前」を付ける🏷️💡

ここが超大事!✨ 名前が付く=責務として独立できる可能性が高いよ😊

例:

  • OrderValidation
  • Pricing(割引・税・合計)
  • Payment
  • OrderRepository
  • OrderNotification
  • OrderAuditLog

この章では、分ける“判断”ができれば勝ち🏆✨ (実際の分割のやり方は9章でガッツリやるよ🧹💖)


8.6 “1つの理由”をもっと上手く言う言い方🎀

SRPを語るとき便利なテンプレだよ👇✨

  • 「このクラスが変わる理由は、◯◯が変わるときです」
  • 「このクラスが責任を持つのは、◯◯という関心事です」
  • 「◯◯の変更は、**△△の担当(アクター)**が決めます」

アクター(誰が変えたいと言うか)で考える説明は、SRPの理解を助けるって話が多いよ👥 (ウィキペディア)


8.7 SRPチェックリスト✅✨(迷ったらこれ)

  • 変更要求が2種類以上ある(税とメール、みたいに)
  • ✅ その変更要求は、同じ人/同じ部署が決めてない
  • ✅ 片方の変更のたびに、もう片方のテストも不安になる😇
  • ✅ 「そしてついでに…」が増えてきた(危険ワード⚠️)
  • ✅ クラス説明が「〜して、〜して、さらに〜する」になってる(盛りすぎ🍰)

8.8 🤖AI活用(Copilot/Codex系)でSRP感覚を爆速にするプロンプト集✨

そのまま貼って使えるやつ置いとくね📎💕

① 変更理由を列挙させる

  • 「このクラスが変更される理由を5〜10個、具体例つきで列挙して」

② アクター(誰の都合)で分類させる

  • 「変更理由を“利害関係者(経理/運用/マーケ等)”ごとに分類して」

③ “分ける単位”の候補名を出させる

  • 「SRPの観点で責務を分割するとしたら、クラス候補名を10個出して」

④ やりすぎ警告もセットで

  • 「分割案のうち、やりすぎ(細かすぎ)になりそうな点も指摘して」

※AIは“提案が得意”だけど、最終判断はあなたが握ってOKだよ〜🫶✨


8.9 ミニクイズ🎯💕

Q1:OrderService に「メール文言の変更」と「税率変更」が入ってるのはSRP的にどう?

  • A:🟥ダメ(変更理由が別)
  • B:🟩OK(どっちも注文処理だから)

✅ 正解:A(税とメールはだいたい別アクターで変わる)👥💥

Q2:「クラスが長い=SRP違反」? ✅ 正解:必ずしもじゃない(長い理由が1つならOKな場合もある)😊


8.10 まとめ🌸✨(次章につながるよ)

  • SRPは「1クラス1機能」じゃなくて、**“1クラス1変更理由”**🎯 (blog.cleancoder.com)
  • 「誰の都合で変わる?」(アクター)で考えると見抜きやすい👥✨ (ウィキペディア)
  • この章では、分ける境界を言語化できれば勝ち🏆💕

次の第9章では、SRPの鉄板パターン **「入力・判断・出力を分ける(I/Oとロジック分離)」**を、実際にリファクタしていくよ〜📥🧠📤✨