第9章 “型で守る”入門:ifチェック地獄から卒業🎓✨

この章は、「不変条件を“型”に引っ越しして、ifチェックの重複を減らす」ための最初の一歩だよ〜😊💕
ちなみに今の最新ラインだと、.NET は .NET 10(LTS)、C# は C# 14 が現行だよ📌✨ (Microsoft for Developers) (この章のサンプルは “いまのC#” で気持ちよく書ける形に寄せていくね🫶)
1) この章のゴール🎯💗
- 「
stringとintだけで全部表す」と何がつらいのか説明できる😵💫 - 「型=安全な箱📦」の感覚が掴める🙂✨
- **プリミティブ地獄(Primitive Obsession)**を “少しずつ” 解消するリファクタ手順が分かる🛠️ (これ、代表的なコードスメルとしてよく挙げられるよ)(リファクタリング・グル )
- そして次章(第10章)の「不正な値を作らせない」にスムーズに入れる🚀✨
2) まず「ifチェック地獄」って何?😇💥
たとえばこんな感じ👇 「メール形式チェック」「空白チェック」「金額が負数じゃないか」…が、あちこちに散らばるやつ😵💫
- 同じチェックがコピペで増える📎
- 片方だけ直して、もう片方は古いまま…が起きる😱
- そもそも「この
stringってメール?ユーザー名?住所?」が分からない🌀
で、バグる💥(そして泣く😭)
3) string/int のままが危ない理由😱🧨
3-1. “意味”が型に乗ってない🫥
string email と書いても、コンパイラ的にはただの string。
Emailっぽい文字列も、UserNameっぽい文字列も、全部 string で区別不能🥲
3-2. “取り違え”がコンパイルで止まらない🙈
たとえば👇
userNameとemailを入れ替えて渡しても、型が同じだから通る😇price(円)とpoint(ポイント)を混ぜても通る😇
3-3. ルールが拡散して、修正が地獄👹
「メールはtrimして小文字にする」と決めたのに、 修正箇所が10箇所あったら…?😵💫
これが Primitive Obsession(プリミティブ偏愛) の典型パターンだよ〜🧟♀️ (リファクタリング・グル )
4) 解決の考え方:「型=安全な箱」📦🛡️
4-1. “外”はゆるく、“中”はかたく🏰✨
- 外(UI入力/HTTP/DB)は、どうしても
stringとかintが来る - でも中(ドメイン)は、壊れた値を入れたくない
だからやることはシンプル👇
✅ 境界(入口)で string/int を “意味ある型” に変換
✅ 中では 意味ある型だけ を使う
5) 型の厚みは3段階あるよ🧁✨
この章では、まず ①② を触って「型で守れる感覚」を作るよ😊 (③は次章でガッツリ!)
- 命名で意味を乗せる(まだ
stringだけど意識が変わる) - 薄いラッパ型(取り違えがコンパイルで止まる🎉)
- 生成を集中して不正値を作れない(Factory/検証/Result ← 第10章)
6) ハンズオン:サブスク登録を “型で” 安全にする💳🌸
題材:サブスク登録(会員 + 月額課金)🎀
6-1. まずは “危ない版” 😇💥
public static void Register(string userName, string email, int monthlyFeeYen)
{
if (string.IsNullOrWhiteSpace(userName)) throw new ArgumentException("userName");
if (string.IsNullOrWhiteSpace(email)) throw new ArgumentException("email");
if (monthlyFeeYen < 0) throw new ArgumentException("monthlyFeeYen");
// どこか別の場所でも同じチェックをまた書く…😵💫
}
この状態だと、こういう事故が起きやすい👇😱
Register(email, userName, monthlyFeeYen); // 入れ替えてもコンパイル通っちゃう😭
6-2. “薄いラッパ型” にして取り違えを止める🛡️✨
まずは「意味の違うものは、型を分ける」だけでOK🙆♀️
public readonly record struct UserName(string Value);
public readonly record struct Email(string Value);
public readonly record struct Yen(int Value);
そして署名を変える👇✨
public static void Register(UserName userName, Email email, Yen monthlyFee)
{
// ここでは「意味が合ってる値が来る前提」で書きやすくなる🙂
// まだこの章では「不正値を作れない」まではやらないよ(次章!)
}
これだけで超でかい進歩💥🎉
Register(new Email("a@b.com"), new UserName("komi"), new Yen(980));
// ↑ もし入れ替えたらコンパイルで止まる✨
✅ 取り違えが“実行時バグ”から“コンパイルエラー”になる これ、めっちゃ強いよ〜🫶💕
6-3. 「じゃあ入力は結局 string で来るんだけど?」問題🤔🧩
うん!来る! だから 境界で変換する(第8章の復習っぽいところ)🚪🔁
たとえば画面入力DTOがこう👇
public sealed class RegisterRequest
{
public string UserName { get; set; } = "";
public string Email { get; set; } = "";
public int MonthlyFeeYen { get; set; }
}
境界で “薄い型” に詰め替える👇
public static void RegisterFromRequest(RegisterRequest req)
{
// ここ(境界)で string/int → 意味のある型 に変換する
var userName = new UserName(req.UserName);
var email = new Email(req.Email);
var fee = new Yen(req.MonthlyFeeYen);
Register(userName, email, fee);
}
この章の到達点はここまででOK🙆♀️✨
次章で「new Email(...) 自体を禁止にして、Createでしか作れない」みたいにしていくよ🏭🔒
7) “型で守る” リファクタ手順(迷子防止マップ🗺️✨)
Step A:ドメイン用語を3つ拾う🔍
例)
- Email(メール)
- UserName(表示名)
- Yen(円)
Step B:まずは「引数/戻り値」から型を差し替える🧷
特におすすめは👇
- 重要な関数の引数(取り違え防止が効く)
- ドメインの戻り値(以降の処理が安全になる)
Step C:境界に “詰め替え” を置く🚪📦
- DTOはゆるく
- ドメインはかたく
Step D:コンパイルエラーを道しるべに進める🧭✨
型を変えるとエラーが出る → そこが “直すべき接続点” コンパイラが案内係になってくれるの、最高だよね😆💕
8) AI活用コーナー🤖💞(案出し係にしよう)
8-1. 「型にした方がいい候補」出してもらう🔍
プロンプト例👇
このC#コードの引数やフィールドのうち、Primitive Obsessionになってそうなものを列挙Remember that。
それぞれ「新しい型名案」「その型が守るべき不変条件」をセットで提案して。
8-2. 「薄いラッパ型」を一気に作ってもらう🏭
次のドメイン用語の薄いラッパ型(record struct)を作って。
UserName, Email, Yen
Valueプロパティ名は Value で統一して。
AIは便利だけど、型名と意味だけはあなたが決めてOKだよ😊🫶 (命名は設計のど真ん中✨)
9) 演習📝🎀(この章はここがメイン!)
演習1:あなたのアプリで「string/int地獄」候補を3つ挙げてね👀
例:
string: Email / UserName / ProductCode / Addressint: Yen / Point / Age / Quantity
「なぜ型にしたいか」を1行で書く✍️✨ (第9章アウトラインの課題そのまま🎓)
演習2:取り違え事故を “コンパイルエラー” に変える🛡️
次のうち1個を選んで、薄い型にしてみてね👇
EmailMoney(Yen)UserName
そして、わざと引数を入れ替えて コンパイルで止まるのを確認👀✨ 「止まった!」が成功体験だよ〜🎉🎉🎉
演習3:境界の詰め替え関数を作る🚪🔁
DTO → 薄い型 → ドメイン関数呼び出し この “1本道” を作ってみよう😊💕
10) まとめ🏁✨
string/intだけで全部表すと、意味が消えて事故る😱- 型を分けるだけで「取り違え」がコンパイルで止まる🎉
- 入口で
string/intを受けて、境界で型に詰め替えるのが基本形🚪📦 - 次章で「不正な値を作れない(new禁止ゾーン)」に進化させるよ🏭🔒
C# 14 / .NET 10 の現行ラインでガンガン書ける設計にしていくね😊✨ (Microsoft Learn)
次に進む前に確認だけ💡
第10章で Email.Create(string) みたいに「作り方を1箇所に固定」していくんだけど、題材はこのまま サブスク課金で統一して進めちゃうね?💳🎀