GoF以外のプログラミング・デザインパターン #14 Specification Pattern

2025/12/05

プログラミング 学習

t f B! P L
eyecatch Specification(スペック)パターンは、DDDの中でも“ビジネスルールを柔軟に表現する”ための重要な設計パターンです。 「条件判定をオブジェクト化」して、複雑な条件ロジックをスッキリ整理する目的で使われます。 複雑な if 文や where 条件をカプセル化し、再利用・組み合わせ可能にするパターンなので、覚えておくと効率化が増すでしょう。 そして、「ビジネスルールの表現力を上げる」ためのパターンと言ってもいいでしょう。

課題に対する目的

このSpecificationパターンは、次のような課題に対応する事を目的としています。 「if 文が増えてコードが読みにくい」場合に、条件をクラスにまとめて読みやすくしたり、 「同じ条件を何度も書いてしまう」場合に、再利用できるようにすることができます。 また、「条件を動的に組み合わせたい」時に、AND / OR / NOT、などで合成できるのが特徴です。

サンプルコード

基本構造

class Specification { isSatisfiedBy(candidate) { throw new Error("must implement isSatisfiedBy()"); } and(other) { return new AndSpecification(this, other); } or(other) { return new OrSpecification(this, other); } not() { return new NotSpecification(this); } } class AndSpecification extends Specification { constructor(left, right) { super(); this.left = left; this.right = right; } isSatisfiedBy(candidate) { return this.left.isSatisfiedBy(candidate) && this.right.isSatisfiedBy(candidate); } } class OrSpecification extends Specification { constructor(left, right) { super(); this.left = left; this.right = right; } isSatisfiedBy(candidate) { return this.left.isSatisfiedBy(candidate) || this.right.isSatisfiedBy(candidate); } } class NotSpecification extends Specification { constructor(spec) { super(); this.spec = spec; } isSatisfiedBy(candidate) { return !this.spec.isSatisfiedBy(candidate); } }

ユーザーの会員資格チェックの実行例

次の条件で実行するサンプルです。
・年齢が18歳以上 ・メール認証済み ・プレミアム会員であること
class AdultSpecification extends Specification { isSatisfiedBy(user) { return user.age >= 18; } } class VerifiedSpecification extends Specification { isSatisfiedBy(user) { return user.emailVerified === true; } } class PremiumSpecification extends Specification { isSatisfiedBy(user) { return user.isPremium === true; } } // 条件を合成 const validMemberSpec = new AdultSpecification() .and(new VerifiedSpecification()) .and(new PremiumSpecification()); // 判定 const user = { age: 25, emailVerified: true, isPremium: false }; console.log(validMemberSpec.isSatisfiedBy(user)); // false

応用例(実際のDDD文脈で)

・OrderCanBeCancelledSpecification(注文キャンセル可能判定) ・UserCanPostArticleSpecification(投稿許可条件) ・ProductIsAvailableSpecification(商品販売可否)
このような、ビジネスのルール名をそのままクラス名にすることで、コードがドメイン言語に近づくのが最大の魅力です。

メリット

・条件ロジックを再利用できる(複数箇所で同じ条件を使える) ・条件を組み合わせやすい(動的なAND/OR構成) ・ビジネスルールを「命名されたオブジェクト」として表現できる ・テストしやすい(条件単体でユニットテスト可能)

デメリット

・クラス数が増える(小規模だとオーバーエンジニアリングになりがち) ・条件が単純なら if 文の方が分かりやすい場合もある ・動的合成に慣れていないと読みづらいコードになることも

構造

[ Entity / ValueObject ] │ ▼ [ Specification ] ← 条件を表現 │ ▼ [ DomainService ] ← 複合ロジックを組み立て │ ▼ [ ApplicationService ] ← フロー制御

あとがき

Class定義の継承(Extends)を使って、クラス命名を自由にした、他人が読んでも分かりやすいプログラムが書きやすくなるパターンですね。 ネスト(条件やループなどの階層構造)を増やさないと言うのは、プログラミングコーディングの基本ですが、それにはいくつかの対処法があります。 今回のSpecificationパターンは、それを解消する1つのパターンです。 さほど難しくない上、コーディングルールとして整えておきたいデザインパターンですね。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ