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

第01章:DbCってなに?「約束」で守る設計🤝📌

1. この章でつかむこと🎯✨

  • DbC(Design by Contract)が「なにをする考え方」なのかを、ふんわりじゃなく説明できるようになる😊🗣️
  • 「契約(約束)」を、メソッドやクラスのルールとして言語化できるようになる📝✨
  • C#で“それっぽく”DbCを始める最小セット(ガード節+軽い保証)を体験する🛡️🎁

1.1 DbCってなに?🤔💡

DbCの全体像:契約による設計

DbC(Design by Contract) は、日本語で「契約による設計」と言うよ😊✨


2. DbCの超ざっくりイメージ:メソッドとクラスの“契約書”📝🤝

DbCは、「呼ぶ側」と「呼ばれる側」が守る“約束”をハッキリ書く設計だよ😊✨ この“約束”があると、バグが起きたときにこうなる👇

  • 「どっちが悪い?」がすぐ分かる💡

    • 呼ぶ側が約束を破った → 呼ぶ側のミス(バグ)🐞
    • 呼ばれる側が約束を守れなかった → 呼ばれる側のミス(バグ)🐞
  • つまり、責任の境界線がはっきりするのがDbCの強みなんだ〜✨🧠

DbCの中心は、だいたいこの3つ(この章では“名前だけ”OK)👇

  • Pre(事前条件)☎️:呼ぶ側が守ること
  • Post(事後条件)🎁:呼ばれた側が保証すること
  • Inv(不変条件)🧱:クラスがいつでも守ること

(C#の最新環境では、C# 13 は .NET 9 SDK と Visual Studio 2022 の最新で試せるよ、という位置づけになってるよ) (Microsoft Learn)


3. 何がうれしいの? DbCの“効き目”3つ🔍✨

3-1. バグの混入点が早く見つかる⚡🐞

約束がコード上にあると、壊れた瞬間に「入口で止められる」ことが増えるよ🚪🛑 結果として、デバッグ時間が短くなる

3-2. コードが読みやすくなる📖🌸

契約は「このメソッド、何を期待して、何を保証するの?」を短く答えてくれる💬 ドキュメントがなくても理解しやすくなるよ😊

3-3. テストの作り方が分かる🧪🎯

Pre/Post/Invが見えると、テストがこう作れる👇

  • Preを破ったらどうなる?(契約違反の検知)
  • Postは本当に満たされる?(正しい保証)
  • Invが壊れない?(モデルの健康診断)🏥🧱

4. よくある誤解:「堅苦しい」じゃなくて「親切」😊💗

誤解A:チェック増える=遅いんじゃ?🐢💦

たしかにチェックは増えるけど、壊れたまま奥へ進むほうが高くつくことが多いよ😵‍💫 入口で止めるのは、トラブルの拡大を防ぐ“保険”みたいなもの🛡️✨

誤解B:DbCって専用機能が必要?🤔

C#には、Eiffelみたいな“言語機能としてのDbC”が標準であるわけじゃないよ🙅‍♀️ でも大丈夫!C#では以下で十分「DbCっぽく」できる👇

  • ガード節(引数チェック)🛡️
  • 例外(契約違反を止める)💥
  • Debug.Assert(内部の前提を壊れた瞬間に気づく)🐞🔔
  • 型(そもそも不正を作れない)💎🔒

昔の「Code Contracts(System.Diagnostics.Contracts)」は、今の .NET 系では“積極採用される路線”ではなく、プロジェクトが事実上止まっている扱いで語られることが多いよ(そのため、まずはガード節中心が現実的) (GitHub)


5. 身近な例で理解しよ!コンビニの“契約”🏪📜✨

たとえば「お酒の販売」って、暗黙のルールがあるよね🍺🔞 これを“契約”にするとこう👇

  • Pre(事前条件)☎️

    • 年齢が20歳以上である
    • 身分証が必要なら提示できる
  • Post(事後条件)🎁

    • 会計が成功したら、レシートが発行される
    • 在庫が1つ減る
  • Inv(不変条件)🧱

    • 在庫は0未満にならない(いつ見ても守られてる)

ポイントはこれ👇 「約束を言葉にできる」=「設計として扱える」ってこと!🧠✨


5.1 自分に合ったスタイルで始めよう🪜😊

DbC導入の3つのステップ

DbCは、一気に全部やろうとしなくて大丈夫🌱


6. C#での“最小DbC”の書き方(まずはガード節)🛡️✨

ここでは「銀行口座から送金する」みたいな例で、入口チェック(Pre)をガード節で書いてみるよ💸🏦

6-1. まずは Pre を入口で止める🚪🛑

using System;
using System.Diagnostics;

public static class TransferService
{
// 「送金する」:最小DbCサンプル
public static void Transfer(decimal amount, string fromAccountId, string toAccountId)
{
// Pre: 呼ぶ側が守るべき条件(入口でチェック)
ArgumentException.ThrowIfNullOrEmpty(fromAccountId);
ArgumentException.ThrowIfNullOrEmpty(toAccountId);

if (fromAccountId == toAccountId)
throw new ArgumentException("from と to は別の口座である必要があります。");

if (amount <= 0)
throw new ArgumentOutOfRangeException(nameof(amount), "amount は 0 より大きい必要があります。");

// ここから下は「条件が満たされている世界」なので、ロジックが読みやすくなる✨

// ...送金処理(例:残高チェック・引き落とし・入金)...

// Post(例):このメソッドが終わると「送金が完了している」ことを保証したい
// ただし、Postの本格運用は第3部で丁寧にやるよ🎁✨

Debug.Assert(amount > 0, "amount は常に正であるべき(内部前提の確認)");
}
}
  • 「ArgumentException.ThrowIfNullOrEmpty」みたいな“ThrowIf〜”系は、ガード節を短く書くための公式APIとして紹介される流れがあるよ🛡️✨ (Steven Giesel)
  • 数値チェックは「ArgumentOutOfRangeException.ThrowIfNegativeOrZero」みたいな形も用意されてる(ジェネリック数値対応のものもある)よ🔢🚫 (Microsoft Learn)

6-2. どうして“入口で”やるの?🌟

入口で止めると、メリットが大きい👇

  • 変な値が中に入らない → 中がシンプルになる🧘‍♀️✨
  • 「どこで壊れた?」が入口で確定しやすい🔍
  • 例外メッセージが“契約の文章”になる💌

7. ミニ演習:コンビニのルールを“契約”にしてみよう🏪✍️✨

次のどれかを1つ選んで、Pre / Post / Inv を日本語で3行書いてみてね😊🌸 (コードじゃなくてOK!まずは言語化が大事📝✨)

A. 返品受付📦 B. 宅配便の受付📮 C. ポイントカードの付与🎫

書き方のテンプレ👇

  • Pre:〜であること
  • Post:成功したら〜になっていること
  • Inv:いつ見ても〜であること

8. AI活用ミニ:ガード節の下書きを作ってもらう🤖🛡️✨

Copilot などに、こう頼むと速いよ⚡️(そのまま採用せず、最後は目で確認ね👀✨)

  • 「このメソッドの引数に対して、自然な Pre(null/空/範囲/同一チェック)をガード節で追加して」
  • 「例外メッセージは、将来読んでも意味が分かる日本語で」

“AIが盛りがち”ポイント👇

  • チェックを増やしすぎて読みにくくする😵‍💫
  • 仕様エラーと契約違反をごっちゃにする🧨(これは第4章で超大事に扱うよ!)

9. この章のまとめ🌸✅

  • DbCは「呼ぶ側/呼ばれる側の約束」を明確にする設計🤝
  • 約束があると、バグが早く見つかって、読むのもテストもラクになる🔍🧪✨
  • C#では、まず ガード節(Pre) から始めるのが現実的で強い🛡️
  • 次章で、Pre / Post / Inv を“3点セット”としてスッキリ整理していくよ🧩✅