第9章:interface入門(DIPの道具を準備)🧰🌟
この章は 「DIPで使う“道具”=interface」 を、いちばんやさしく・実戦寄りで身につける回だよ〜!😄💕
9.0 この章のゴール🎯✨
読み終わったら、こんな状態になれるよ👇
- interface を 「できることの約束🤝」 として説明できる
- 「どんな interface を切ればいいか(責務の決め方)」が分かる📦
- interface が肥大化🐘💦 しないための考え方が分かる
- 最小の
IUserRepositoryを自分で設計できる✨
9.1 interfaceってなに?(超シンプルに言うと…)🤝✨
interface は 「これができるよ!」っていう “できることリスト” だよ📋😊 使う側(上位)のコードは “中身” を知らなくてOK。約束(interface)だけ 見て使えるのが強み💪
Microsoft の公式ドキュメントでも、interface は 契約(contract) で、実装クラスはそのメンバーを実装する…って整理されてるよ。(Microsoft Learn)
9.2 interfaceで「できる/できない」こと(C# 14の今どき事情)🧠✨
いまのC#(C# 14)では interface はかなり強化されてて、デフォルト実装 や static メンバー も書けるようになってるよ〜!😳✨ ただし!DIPの入門では まず “王道の使い方” だけ でOK🙆♀️
✅ まず覚えるルール(入門セット)🌷
- interface は 「メソッド・プロパティの宣言」 が中心
- interface 自体は 状態(フィールド)を持てない(= データを抱え込まない)(Microsoft Learn)
- 実装は class 側が持つ(ここが “差し替え” を可能にする✨)
ちなみに C# 14 は .NET 10 でサポートされる “最新” のC#だよ。(Microsoft Learn)
9.3 DIP目線でのinterface:決めるのは「使う側」👑➡️📄
DIPの世界で大事なのはここ👇✨
interface は 「実装したい側(DB担当)」が決めるものじゃなくて、 「使う側(業務ロジック)」が欲しい操作を表現するもの 😍
だから、interface名やメソッド名も DB都合にしない のがコツ!🧼✨ 例:
- ❌
ISqlUserRepository(DBが名前に入ってる=下位都合っぽい) - ✅
IUserRepository(業務が欲しい “ユーザーの保存・取得” の約束)
9.4 例:IUserRepository の責務を決める📦✨(最小でいこう!)
ここでは「ユーザー登録」を例にするよ〜!🧁 要件はこんな感じ👇
- 既に同じメールがあったら登録NG🙅♀️
- なければ登録OK👌
この要件に必要な操作って、実は 2つだけ なんだよね✨
- メールで探す
- 追加する
だから interface も最小でこうなる👇(気持ちいい〜!😆)
using System.Threading;
using System.Threading.Tasks;
public interface IUserRepository
{
Task<User?> FindByEmailAsync(string email, CancellationToken ct = default);
Task AddAsync(User user, CancellationToken ct = default);
}
public sealed record User(string Email, string DisplayName);
ポイント🌟
- “登録” に必要な操作だけ に絞ってる(=肥大化しにくい)
CancellationTokenは “今どきの非同期の礼儀” くらいの感覚でOK👌(慣れたらで大丈夫!)
9.5 実装は何個あってもOK(差し替えの土台)🔁✨
interface があると、実装は好きに増やせるよ😄 例えばメモリ版(テスト用)👇
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public sealed class InMemoryUserRepository : IUserRepository
{
private readonly List<User> _users = new();
public Task<User?> FindByEmailAsync(string email, CancellationToken ct = default)
{
var user = _users.FirstOrDefault(u => u.Email == email);
return Task.FromResult(user);
}
public Task AddAsync(User user, CancellationToken ct = default)
{
_users.Add(user);
return Task.CompletedTask;
}
}
DB版も作れる(ここは中身がゴリゴリになってOK😈🗄️) でも 上位(業務ロジック)は interface だけ見てればOK になるのがDIPの美味しいところ🍰✨
9.6 罠:interfaceが肥大化する🐘💦(あるある!)

DIPを学びはじめると、みんな一度はこうなる🤣👇
「RepositoryってCRUD全部いるよね?」
→ GetAll / Search / Update / Delete / BulkImport / Count / ...
→ いつの間にか 巨大interface に…😇
🧨 肥大化interfaceの例(悪い見本)
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public interface IUserRepository
{
Task<User?> FindByIdAsync(Guid id, CancellationToken ct = default);
Task<User?> FindByEmailAsync(string email, CancellationToken ct = default);
Task<List<User>> SearchAsync(string keyword, CancellationToken ct = default);
Task AddAsync(User user, CancellationToken ct = default);
Task UpdateAsync(User user, CancellationToken ct = default);
Task DeleteAsync(Guid id, CancellationToken ct = default);
Task<int> CountAsync(CancellationToken ct = default);
Task BulkImportAsync(IEnumerable<User> users, CancellationToken ct = default);
}
😱 何が困るの?
- 使わないメソッドが増えて 読むだけで疲れる 😵💫
- 実装クラスが「全部実装しろ」になって 地獄 🔥
- 変更が入ると、影響範囲がでかくなる(DIPの逆効果!)💥
9.7 肥大化を防ぐコツ(初心者に効く3つ)🛡️✨

コツ①:“ユースケース起点” で切る🎬
「登録に必要な操作は何?」 「ログインに必要な操作は何?」 この問いが最強💪✨
コツ②:読み取りと書き込みで分割する📖✍️
まずはこの分け方が分かりやすいよ〜!
using System.Threading;
using System.Threading.Tasks;
public interface IUserReader
{
Task<User?> FindByEmailAsync(string email, CancellationToken ct = default);
}
public interface IUserWriter
{
Task AddAsync(User user, CancellationToken ct = default);
}
実装クラスは両方を実装してもOK👌
public sealed class InMemoryUserRepository : IUserReader, IUserWriter
{
// 中身はさっきの実装を流用できるよ
}
コツ③:「名前」に下位都合を入れない🚫🗄️
- ❌
IFileUserRepository(ファイル都合が見えてる) - ✅
IUserRepository/IUserReader(業務の言葉)
9.8 interfaceの命名ルール(迷ったらこれ)🧾✨
C#の世界では interfaceは I プレフィックス が基本だよ〜!😊
Microsoft の設計ガイドラインでも推奨されてる✨(Microsoft Learn)
IUserRepository(名詞)IUserReader(名詞)IPersistable(形容詞)みたいなのもOK(慣れたらで🙆♀️)
そして 実装クラスは I を外す のが王道👇
IUserRepository←→UserRepositoryIUserReader←→UserReader
9.9 ちょい発展:デフォルト実装はいつ使う?🧩✨
interface には デフォルト実装 を入れられる(=後からメンバー追加しても壊しにくい)という強い技があるよ。(Microsoft Learn) ただ、最初はこう覚えてOK👇
- ✅ チーム/自分のアプリ内:そもそもinterfaceを小さく保つ(今回の方針)
- ✅ 外部に配布するライブラリ:デフォルト実装が役立つ場面がある
- 🔰 入門のうちは:使わなくてOK(混乱しやすいからね🌀)
9.10 Visual Studioの“楽する機能”🪄✨(覚えると超気持ちいい)
✅ クラスからinterfaceを切り出す(Extract Interface)
- クラス名にカーソル
- ショートカットで リファクタリング
- 「このメソッドだけinterfaceにしたい」を選べる✨
✅ interfaceを実装させてスタブ自動生成(Implement Interface)
: IUserRepositoryって書く- 自動でメソッド骨組みが出る(神🥹✨)
(この2つは、手で書くよりミスが減るし速いよ〜!🏎️💨)
9.11 演習(手を動かすと一気に分かるよ)✍️😆
演習A:最小interfaceを設計してみよう🧠✨
次のユースケースで、必要なメソッドを2つ以内 にしてみてね👇
-
「パスワード再発行」
- メールでユーザーを探す
- 再発行トークンを保存する
👉 あなたの答え(例)
FindByEmailAsyncSaveResetTokenAsync
演習B:肥大化interfaceを分割しよう🐘➡️🧩
巨大な IUserRepository を見つけたら、まず
IUserReaderIUserWriterに割ってみる(それだけで世界が平和になること多いよ🕊️✨)
9.12 Copilot / Codexに手伝わせるプロンプト例🤖💡(安全運転つき)
✅ 1) “最小interface案”を出させる
「メソッドは最大2〜3個」って縛るのがコツ!🎀
- 「ユーザー登録のユースケースに必要な最小C# interfaceを提案して。メソッドは最大2つ。命名は業務寄りで、DBやHTTPなど実装都合の単語は入れないで。」
✅ 2) 肥大化を検知して分割案を出させる
- 「このinterfaceが肥大化しているかチェックして、分割案(IUserReader/IUserWriterなど)を3案出して。分割理由も1行ずつ。」
✅ 3) Fake実装(テスト用)を作らせる
- 「このinterfaceのInMemory実装を作って。例外条件(重複メール)も再現できるようにして。」
まとめ🎀✨(この章で一番大事!)
- interface は 「できることの約束」 🤝
- DIPでは interface は 使う側(業務)が欲しい操作 で決める👑
- まずは 最小メソッド数 で作る(肥大化を防ぐ)🧼✨
- 肥大化したら Reader/Writer分割 が効く📖✍️
- 命名は
Iプレフィックスが王道🧾(Microsoft Learn)
次の第10章では、いよいよこの IUserRepository を使って “最小DIP” を完成 させるよ〜!🎉🔁💖