第20章:三角測量(2ケース目で一般化)📐
この章のゴール🎯

- 「最初はベタでもOK」→ 2つ目の例で“ルール化(一般化)”する流れが体に入る🧠✨
- **“一般化が早すぎて複雑化”**を避けられるようになる🚧💦
- 次に足すべきテストケースを、自分で選べるようになる🎲✅
三角測量ってなに?🤔📐
ざっくり言うとこう👇
- 1個目のテスト:とりあえず通す(仮実装・ベタ実装でもOK)🩹
- 2個目のテスト:別の例を追加して、**「あ、共通のルールが必要だ」**って状況を作る✨
- そこで初めて、ルールとして一般化する📐✅
ポイントは、**“一般化は2例目が来てから”**ってところだよ〜!😄✨
なぜ三角測量が効くの?🧠🚦
TDDをやってると、最初のうちって「この先どうなるか分からん…😵」ってなることあるよね。
そこでいきなり完璧な設計をしようとすると…
- まだ要らない抽象化が増える😇
- クラスが増えて迷子になる🧳💦
- 仕様変更に弱くなる(悲しい)🥲
三角測量はそれを防いで、**“必要になった瞬間にだけ、ちょうどよく一般化する”**ための技だよ📐✨
ハンズオン:ミニ題材「税を足す」☕️🧾(三角測量の最短コース!)
今回の超ミニ仕様📋✨
AddTax(subtotal)は 10%の税を足した金額を返す- 端数とか丸めは今回は触れない(まずは三角測量に集中!)😌🫶
Step 1:まずは1本目のテストを書く(Red🔴)
テストは「例」を1つだけ置くよ〜👇
using Xunit;
public class PriceCalculatorTests
{
[Fact]
public void AddTax_100yen_returns_110yen()
{
// Arrange
var calc = new PriceCalculator();
// Act
var total = calc.AddTax(100);
// Assert
Assert.Equal(110, total);
}
}
まだ PriceCalculator が無いから当然落ちる!🔴✨
この「落ちた!」が、TDDのスタート合図だよ〜🚦😆
Step 2:最短で通す(Green🟢)=仮実装OK🩹
ここ、わざとベタにいくよ👇(わざと!)
public class PriceCalculator
{
public int AddTax(int subtotal)
{
return 110; // 仮実装(100円のケースだけ通す)
}
}
✅ テストが通ったら勝ち!🟢🎉 ここで「ちゃんと式を書かなきゃ!」って焦らないでOKだよ🙆♀️✨ (次の一手で“必要になったら”一般化するからね📐)
Step 3:2本目のテストを追加する(Red🔴)=“一般化が必要な状況”を作る📐
次は 別の例 を置くよ〜👇
[Fact]
public void AddTax_200yen_returns_220yen()
{
// Arrange
var calc = new PriceCalculator();
// Act
var total = calc.AddTax(200);
// Assert
Assert.Equal(220, total);
}
今の実装は 110 固定だから、当然落ちる!🔴😆
はい、ここでやっとこう思う👇
「あ、
110固定じゃダメだ。共通ルール(一般化)が必要だ…!」📐✨
これが三角測量の「測量できた瞬間」だよ〜🥳
Step 4:一般化する(Green🟢➡️📐)
2例そろったので、やっと式にする!✨
public class PriceCalculator
{
public int AddTax(int subtotal)
{
return subtotal + (subtotal / 10); // 10%(今回は割り切れる値だけで進めてる)
}
}
✅ 2本とも通った!🟢🎉 この瞬間が最高に気持ちいいやつ〜〜😆✨
Step 5:Refactor(整理)🧹✨(今やるのは“読みやすさ”中心)
ここは小さく整えるだけでOK🙆♀️ たとえば「10%」が分かるように名前を付けるとか👇
public class PriceCalculator
{
private const int TaxRatePercent = 10;
public int AddTax(int subtotal)
{
return subtotal + (subtotal * TaxRatePercent / 100);
}
}
✅ テストがあるから、安心して置き換えできる🛡️✨ (Refactorのたびにテスト回すの忘れないでね〜🔁🧪)
三角測量の「テストケース選び」コツ集🎯✨
コツ1:2本目は“同じ実装で通らない例”を選ぶ📌
- 今回なら
100→110の次に200→220を置いたから、固定値が死ぬ💀✨ - もし
100→110の次に110→121とかだと、偶然通る実装が残ることもある😇💦
コツ2:迷ったら「真逆」を置く🧲
- 境界の反対側(小さい/大きい、0/正、内側/外側)を置くと、ルールが見えやすい👀✨
コツ3:2例でもルールが確定しないなら、3例目を足す🧩
- 「AかBか分からん…」ってなったら、決め手になる3例目を置くのが正解🙆♀️✨
よくあるミスあるある😵💫💦(ここ超大事)
- 1本目から一般化しすぎる(まだ要件が見えてないのに抽象化して迷子)🧳
- 2本目も似すぎてて一般化が起きない(固定値でも通っちゃう)😇
- “テスト2本まとめて”書いてから実装する(TDDのリズムが崩れやすい)🥲
- Refactorで一気に変えすぎる(小さく置き換え→テスト、が安全)🛡️🔁
AIの使いどころ(この章のおすすめ)🤖✨
AIは「次の一手」を出すのが得意だよ〜😆🫶 ただし採用条件は テストが決める✅🧪
使い方1:次に足すべきテストケースを出させる🎲
- 「この仕様で“2本目”に最適な例を3つ提案して。固定値実装を殺せるやつで」
使い方2:2例で曖昧なときの“決め手の3例目”を出させる🧩
- 「この2例だとルールが複数ありえる。分岐を潰す3例目を提案して」
使い方3:リファクタ案を“小さく”出させる🧹
- 「このコードを読みやすくする最小のリファクタ案を3つ。変更は小さく」
ミニ演習(今日の宿題)🧪💪🎀
演習A:送料計算📦✨
仕様:
ShippingFee(totalPrice)totalPrice < 3000なら送料500totalPrice >= 3000なら送料0
やり方(これが三角測量の型!)👇
- まず
1000 -> 500をテスト(仮実装でもOK)🩹 - 次に
3000 -> 0をテスト(境界を刺す!🎯) - 一般化(ifでOK、これは“仕様のルール”だから正義🙆♀️)✨
演習B:ポイント計算🎁✨(余裕ある人向け)
仕様:
Points(amount)はamount / 100(整数)ポイント- 例:
100 -> 1,250 -> 2
1例目で仮実装→2例目で一般化、やってみてね📐🧪
セルフチェック✅✨(これ全部Yesなら勝ち!)
- 1例目はベタ実装でもOKだと分かる🩹
- 2例目で「一般化が必要」になる感覚がある📐
- 2例目は“固定値を殺す”ように選べる💀
- 2例で曖昧なら3例目を足す判断ができる🧩
- Refactorは小さく、テストをこまめに回せる🔁
- AIは「案出し」に使い、採用はテストで決める✅🧪
さいごに:今日のまとめ💡🎉
三角測量は「設計を育てる」ための超いい技だよ〜📐✨ **“必要になるまで一般化しない”**って、実はめちゃ強い!😆🫶
最新メモ(2026-01-18時点)🗞️✨
- .NET 10 の最新SDKは **10.0.2(2026-01-13更新)**だよ。 (Microsoft)
- C# 14 は .NET 10 でサポートされてるよ。 (Microsoft Learn)
- xUnit v3 の最新安定版は 3.2.2(NuGet/公式リリースノート)。 (xUnit.net)
- Visual Studio 2026 は .NET 10 / C# 14 をサポートする前提のリリース情報が出てるよ。 (Microsoft Learn)
次の章(第21章:明白な実装🌼)に行く前に、もしよければこの章の演習A(送料計算📦)を“テスト→仮実装→2本目→一般化”の流れで一緒に作って、コミット単位まで整えるところまでやろう〜!😆🧪✨