第18章:条件式の分解(Explain Condition)🧩📝
ねらい🎯
複雑で読みにくい if の条件式を、**意味が伝わる“言葉”**に分解して、コードをスラスラ読める状態にします📖✨
ポイントはこれ👇
- 条件式を 「説明できる名前」 にする🏷️
- 否定・二重否定を減らして 読み間違いを防ぐ 🙅♀️
- 「何を判定してるの?」を 関数名で語らせる 🗣️
※C# 14 は .NET 10 と Visual Studio 2026 で試せます🌟 (Microsoft Learn)
Explain Condition って何?💡

「Explain Condition」は、リファクタリングの定番テクです✨ やることはシンプル👇
- ごちゃごちゃした条件式を
✅ 中間変数(説明変数) にする
✅ 判定メソッド(
Is〜) にする - その結果、
ifの行が「文章」みたいに読めるようになる📚✨
この系統は “Decompose Conditional(条件の分解)” としても知られていて、条件を summer() みたいな判定関数に置き換える形が紹介されています📌 (リファクタリング)
なんで条件式って読めなくなるの?🤯
あるある原因はこのへん👇
&&と||が長い行に詰め込まれてる📛!(否定)が増えて、頭の中で反転し続ける🌀nullチェックや境界条件が混ざってる🧷- 「本当は3つの判定」なのに、1行に全部入ってる🍱💦
読む側の脳内でこうなる…👇
「えっと…この ! は反転して…|| の優先順位は…」😵💫
Before → After で体感しよう👀✨
Before:条件が“暗号”になってる例🧩💦
「送料無料かどうか」を決めたいだけなのに、条件がつらい…!
public static decimal CalculateShippingFee(Order order, Customer? customer)
{
if ((customer != null && customer.IsVip && order.TotalAmount >= 10_000m)
|| (order.TotalAmount >= 20_000m && order.DestinationCountry == "JP" && !order.HasHazardousItems)
|| (order.Items.Count == 0 && order.IsGift && customer != null && !customer.IsBlacklisted))
{
return 0m;
}
return 800m;
}
public sealed class Order
{
public decimal TotalAmount { get; init; }
public string DestinationCountry { get; init; } = "";
public bool HasHazardousItems { get; init; }
public List<string> Items { get; } = new();
public bool IsGift { get; init; }
}
public sealed class Customer
{
public bool IsVip { get; init; }
public bool IsBlacklisted { get; init; }
}
Step 1:まずは「説明変数」で分解しよう🏷️✨
いきなりメソッド化せず、まずは bool 変数で「意味」を付けます📝
public static decimal CalculateShippingFee(Order order, Customer? customer)
{
bool isVip = customer?.IsVip == true;
bool isBlacklisted = customer?.IsBlacklisted == true;
bool vipFreeShipping = isVip && order.TotalAmount >= 10_000m;
bool largeDomesticOrder =
order.TotalAmount >= 20_000m
&& order.DestinationCountry == "JP"
&& !order.HasHazardousItems;
bool emptyGiftOrder =
order.Items.Count == 0
&& order.IsGift
&& customer is not null
&& !isBlacklisted;
bool isFreeShipping = vipFreeShipping || largeDomesticOrder || emptyGiftOrder;
return isFreeShipping ? 0m : 800m;
}
ここが大事💖
vipFreeShippingみたいに 「true の意味」が読める名前にする🏷️- 先に
isVipを作ると、customer != null && customer.IsVipを何回も書かなくて済む😊 - 最後に
isFreeShippingでまとめると、ifの意図が一瞬でわかる👀✨
Step 2:「Is〜」判定メソッドにして“文章化”する🗣️✨
条件が複数箇所で使われそう、またはルールとして名前を固定したいなら 判定メソッド化が強いです💪
public static decimal CalculateShippingFee(Order order, Customer? customer)
{
return IsFreeShippingEligible(order, customer) ? 0m : 800m;
}
private static bool IsFreeShippingEligible(Order order, Customer? customer)
{
return IsVipFreeShipping(order, customer)
|| IsLargeDomesticOrderFreeShipping(order)
|| IsEmptyGiftOrderFreeShipping(order, customer);
}
private static bool IsVipFreeShipping(Order order, Customer? customer)
=> customer is { IsVip: true } && order.TotalAmount >= 10_000m;
private static bool IsLargeDomesticOrderFreeShipping(Order order)
=> order.TotalAmount >= 20_000m
&& order.DestinationCountry == "JP"
&& !order.HasHazardousItems;
private static bool IsEmptyGiftOrderFreeShipping(Order order, Customer? customer)
=> order.Items.Count == 0
&& order.IsGift
&& customer is { IsBlacklisted: false };
読み心地がこう変わる😍
IsFreeShippingEligible(...) ← 何をしたいかが即わかる
中の条件は、ルールごとに名前が付いてるから迷わない🧭✨
Step 3:否定(!)を減らすコツ🙅♀️➡️🙂
否定が増えると、読み間違いが増えます💦 できるだけ 肯定形に寄せるのがおすすめ🌸
- ❌
!isNotValid(二重否定みたいで混乱) - ✅
isValid - ❌
!(a && b) - ✅
!a || !bに変形…よりも ✅ そもそもIs〜メソッドに隠す(読み手の脳内負担ゼロ)✨
「どこまで分解する?」の目安📏✨
分解しすぎも逆に読みにくくなるので、目安を持つと安心です😊
✅ 変数で十分なケース
- その
ifだけでしか使わない - “一瞬で意味が通る” くらいの複雑さ
- 変更されにくい単発条件
✅ メソッドにしたいケース
- 同じ条件が複数箇所で登場してる🔁
- ビジネスルールとして名前を固定したい📜
- 条件が3行以上で「読むのに息継ぎが必要」😮💨
安全に進める手順🛡️🔁
条件式の変更はバグりやすいので、小さくいきます💓
- ✅ 先に動作確認(テスト or 実行でOK)
- ✅ 条件式を 説明変数にする(動作は変えない)
- ✅ 変数名を整える(読みやすさUP)
- ✅ 必要なら 判定メソッド化
- ✅ 最後にもう一回、必ず動作確認✅
ミニテスト例(挙動が変わってないか確認)🧪✨
「VIP で 10,000円以上なら送料無料」の確認だけでも保険になります🛟
using Xunit;
public sealed class ShippingTests
{
[Fact]
public void Vip_and_10000_or_more_is_free_shipping()
{
var order = new Order { TotalAmount = 10_000m, DestinationCountry = "JP" };
var customer = new Customer { IsVip = true };
var fee = CalculateShippingFee(order, customer);
Assert.Equal(0m, fee);
}
// この章のサンプルをここに貼る想定
private static decimal CalculateShippingFee(Order order, Customer? customer)
=> IsFreeShippingEligible(order, customer) ? 0m : 800m;
private static bool IsFreeShippingEligible(Order order, Customer? customer)
=> customer is { IsVip: true } && order.TotalAmount >= 10_000m;
}
Visual Studio で爆速にやる🪄💻
「分解」は IDE のリファクタ機能を使うと超ラクです✨
- Extract Method(メソッド抽出)
キーボード:
Ctrl+R→Ctrl+M(プロファイルで違うことあり) (Microsoft Learn) - Rename(名前変更)
キーボード:
Ctrl+R→Ctrl+R(Microsoft Learn) - Introduce Local Variable(ローカル変数抽出)
まず
Ctrl+.(Quick Actions)で候補が出せるよ🪛✨ (Microsoft Learn) - ショートカット一覧(Refactor 系がまとまってて便利) (Microsoft Learn)
AI拡張(Copilot / OpenAI系)での使いどころ🤖✨
AIは「意味の言語化」と「命名案」が得意です💖 ただし、採用前に必ず差分とテストで確認ね✅
使える頼み方例🗣️
- 「この条件式が true のとき、日本語で何が起きてる?」📝
- 「説明変数名を5案。肯定形で、短めで!」🏷️
- 「
Is〜判定メソッドに分解して。副作用がない前提で」🧩 - 「二重否定を減らして読みやすくして」🙂
ミニ演習📝✨(手を動かすよ〜!)
次のコードを、2〜3個の判定関数に分解してみよう✂️💕
演習コード
public static bool CanCheckout(User? user, Cart cart)
{
return user != null
&& !user.IsSuspended
&& cart.Items.Count > 0
&& (cart.TotalAmount >= 3000m || user.IsPremium)
&& !(cart.HasAgeRestrictedItems && user.Age < 18);
}
public sealed class User
{
public bool IsSuspended { get; init; }
public bool IsPremium { get; init; }
public int Age { get; init; }
}
public sealed class Cart
{
public List<string> Items { get; } = new();
public decimal TotalAmount { get; init; }
public bool HasAgeRestrictedItems { get; init; }
}
ゴール例(こんな感じになればOK)🌸
IsActiveUser(user)HasItems(cart)IsEligibleByPriceOrPremium(user, cart)IsAllowedForAgeRestriction(user, cart)
よくある落とし穴⚠️
-
条件の中に 関数呼び出しがあるとき
- その関数が重い / 副作用ありだと、分解でタイミングが変わることがある💦
-
&&/||の 短絡評価(途中で評価が止まる)を壊さないように注意⚡ -
nullを含む条件は、?.やパターン(is { ... })で安全に🧷
まとめチェックリスト✅✨
- 条件式を読んで「何の判定?」が1秒で言える🙂
-
!が多すぎない(否定まみれになってない)🙅♀️ -
Is〜/Has〜など、true の意味が明確な名前になってる🏷️ - 変更は小さく、動作確認できてる✅
-
ifが“文章”として読めるようになってる📖✨
参考(リファクタリングの代表パターン)📚✨
- Decompose Conditional(条件の分解) (リファクタリング)
- リファクタリングは「外部動作を変えずに内部構造を改善する」小さな変換の積み重ね (martinfowler.com)