導入
if (logger != null) logger.Log(...) という null チェックがコード中に散らばると読みにくくなります。Null Object パターンは「何もしない実装」を用意しておき、null の代わりにそれを使うことで、呼び出し側の分岐を消します。
図解
flowchart LR
C["呼び出し側"] -->|null チェック不要| I["ILogger"]
I -.-> R["本物: 実際に記録"]
I -.-> N["NullLogger: 何もしない"]
style N fill:#eeeeee
サンプル
Run(new ConsoleLogger()); // [LOG] 開始 / [LOG] 終了
Run(NullLogger.Instance); // 何も出ない(null チェックなしで安全)
void Run(ILogger logger)
{
logger.Log("開始");
logger.Log("終了");
}
interface ILogger { void Log(string message); }
class ConsoleLogger : ILogger { public void Log(string m) => Console.WriteLine($"[LOG] {m}"); }
class NullLogger : ILogger
{
public static readonly NullLogger Instance = new();
public void Log(string message) { /* 何もしない */ }
}
- 「何もしない実装(Null Object)」を用意しておく
- null の代わりにそれを渡せば、呼び出し側は
if (x != null)を書かなくてよい - 存在しない担当者・空の設定などを安全に表現できる
やってみよう
Run に null を渡すとどうなるか(NullReferenceException)を確認し、NullLogger.Instance に替えると分岐なしで安全に動くことを比べましょう。
演習
interface INotifier { void Notify(string msg); }
// TODO: 何もしない NullNotifier を定義してください(Notify は空でよい)
___
void Process(INotifier n) { n.Notify("done"); Console.WriteLine("処理完了"); }
Process(new NullNotifier()); // 処理完了(通知は何も起きない)
- 期待される出力:
処理完了
ヒント1を見る
class NullNotifier : INotifier { public void Notify(string msg) { } }
ヒント2を見る
Notify の中身を空にすれば「何もしない」実装になります
まとめ
- Null Objectは「何もしない実装」で null チェックを消す
- 呼び出し側の分岐が減り、NullReference事故を防ぐ
- 「担当なし」「空の設定」を安全に表せる
次回: 条件をオブジェクト化する Specification パターンです。