本文へスキップ
BecomeCoder

C#デザインパターンコース · 第3章 現代の設計パターン · レッスン26

Specification パターン

ブラウザで完結

導入

「有効な会員」「送料無料の注文」——業務の条件があちこちに if で散らばると、同じ判定を何度も書くことになります。Specification パターンは、条件を「判定できるオブジェクト」に切り出し、名前を付けて再利用・組み合わせられるようにします。

図解

flowchart LR
    S1["ISpecification<Order>"] --> A["IsSatisfiedBy(order) → true/false"]
    S1 -.-> B["高額注文の仕様"]
    S1 -.-> C["優先会員の仕様"]
    B & C --> D["組み合わせて再利用"]
    style S1 fill:#e1f5fe

サンプル

var orders = new List<Order>
{
    new Order(1500), new Order(500), new Order(3000)
};

var expensive = new ExpensiveSpec(1000);   // 「1000円超」という条件をオブジェクト化
var picked = orders.Where(expensive.IsSatisfiedBy).ToList();
Console.WriteLine(picked.Count);           // 2

record Order(int Amount);

interface ISpecification<T> { bool IsSatisfiedBy(T target); }
class ExpensiveSpec : ISpecification<Order>
{
    private readonly int _threshold;
    public ExpensiveSpec(int threshold) => _threshold = threshold;
    public bool IsSatisfiedBy(Order o) => o.Amount > _threshold;
}
  • 条件を IsSatisfiedBy を持つオブジェクトに切り出す
  • 名前が付くので意図が明確(ExpensiveSpec)で、Where などにそのまま渡せる
  • 仕様どうしを AND/OR で組み合わせて再利用できる

やってみよう

Amount がちょうど割り切れる等の別の仕様クラスを作り、orders.Where(spec.IsSatisfiedBy) に差し替えて結果の違いを見ましょう。

演習

record Person(int Age);
interface ISpecification<T> { bool IsSatisfiedBy(T target); }

// TODO: Age が 18 以上なら true を返す AdultSpec を実装してください
___

var people = new List<Person> { new Person(15), new Person(20), new Person(40) };
var adults = people.Where(new AdultSpec().IsSatisfiedBy).Count();
Console.WriteLine(adults);   // 2
  • 期待される出力: 2
ヒント1を見る

class AdultSpec : ISpecification<Person> { public bool IsSatisfiedBy(Person p) => p.Age >= 18; }

ヒント2を見る

IsSatisfiedBy は判定結果の bool を返します

まとめ

  • Specificationは業務条件を「判定できるオブジェクト」に切り出す
  • 名前が付き、再利用・組み合わせ(AND/OR)ができる
  • 散らばった if を1か所に集約できる

次回: 例外に頼らずに失敗を表す Result/Option です。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。