
コンピュータプログラミングにおいて、仕様パターンとは、特定のソフトウェア設計パターンであり、ブール論理を用いてビジネスルールを連結することで、ビジネスルールを再構成できるものです。このパターンは、ドメイン駆動設計の文脈で頻繁に使用されます。
仕様パターンは、他のビジネスルールと組み合わせ可能なビジネスルールを概説します。このパターンでは、ビジネスロジックの単位は、抽象集約クラスである Composite Specification クラスから機能を継承します。Composite Specification クラスには、ブール値を返す IsSatisfiedBy という関数が 1 つあります。インスタンス化後、仕様は他の仕様と「連鎖」されるため、新しい仕様は保守しやすく、かつ高度にカスタマイズ可能なビジネスロジックになります。さらに、インスタンス化時に、ビジネスロジックは、メソッド呼び出しまたは制御の反転によって、永続化リポジトリなどの他のクラスのデリゲートとなるように状態を変更することができます。
高レベルのビジネスロジック/ドメインロジックをランタイムで合成する結果として、仕様パターンは、アドホックなユーザー検索条件をリポジトリで処理される低レベルのロジックに変換するための便利なツールとなります。
仕様は再利用可能な形式でロジックをカプセル化したものであるため、徹底的な単体テストが非常に容易であり、この文脈で使用される場合は、シンプルなオブジェクトパターンの実装にもなります。
public interface ISpecification { bool IsSatisfiedBy ( object candidate ); ISpecification And ( ISpecification other ); ISpecification AndNot ( ISpecification other ); ISpecification Or ( ISpecification other ); ISpecification OrNot ( ISpecification other ); ISpecification Not (); }public abstract class CompositeSpecification : ISpecification { public abstract bool IsSatisfiedBy ( object candidate );public ISpecification And ( ISpecification other ) { return new AndSpecification ( this , other ); }public ISpecification AndNot ( ISpecification other ) { return new AndNotSpecification ( this , other ); }public ISpecification Or ( ISpecification other ) { return new OrSpecification ( this , other ); }public ISpecification OrNot ( ISpecification other ) { return new OrNotSpecification ( this , other ); }public ISpecification Not () { return new NotSpecification ( this ); } }public class AndSpecification : CompositeSpecification { private ISpecification _leftCondition ; private ISpecification _rightCondition ;public AndSpecification ( ISpecification left , ISpecification right ) { _leftCondition = left ; _rightCondition = right ; }public override bool IsSatisfiedBy ( object candidate ) { return _leftCondition.IsSatisfiedBy ( candidate ) && _rightCondition.IsSatisfiedBy ( candidate ) ; } }public class AndNotSpecification : CompositeSpecification { private ISpecification _leftCondition ; private ISpecification _rightCondition ;public AndNotSpecification ( ISpecification left , ISpecification right ) { _leftCondition = left ; _rightCondition = right ; }public override bool IsSatisfiedBy ( object candidate ) { return _leftCondition.IsSatisfiedBy ( candidate ) && _rightCondition.IsSatisfiedBy ( candidate ) ! = true ; } }public class OrSpecification : CompositeSpecification { private ISpecification _leftCondition ; private ISpecification _rightCondition ;public OrSpecification ( ISpecification left , ISpecification right ) { _leftCondition = left ; _rightCondition = right ; }public override bool IsSatisfiedBy ( object candidate ) { return _leftCondition.IsSatisfiedBy ( candidate ) || _rightCondition.IsSatisfiedBy ( candidate ) ; } }public class OrNotSpecification : CompositeSpecification { private ISpecification _leftCondition ; private ISpecification _rightCondition ;public OrNotSpecification ( ISpecification left , ISpecification right ) { _leftCondition = left ; _rightCondition = right ; }public override bool IsSatisfiedBy ( object candidate ) { return _leftCondition.IsSatisfiedBy ( candidate ) || _rightCondition.IsSatisfiedBy ( candidate ) ! = true ; } }public class NotSpecification : CompositeSpecification { private ISpecification _wrapped ;public NotSpecification ( ISpecification x ) { _wrapped = x ; }public override bool IsSatisfiedBy ( object candidate ) { return ! _wrapped.IsSatisfiedBy ( candidate ) ; } }public interface ISpecification < T > { bool IsSatisfiedBy ( T candidate ); ISpecification < T > And ( ISpecification < T > other ); ISpecification < T > AndNot ( ISpecification < T > other ); ISpecification < T > Or ( ISpecification < T > other ); ISpecification < T > OrNot ( ISpecification < T > other ); ISpecification < T > Not (); }public abstract class LinqSpecification < T > : CompositeSpecification < T > { public abstract Expression < Func < T , bool >> AsExpression (); public override bool IsSatisfiedBy ( T candidate ) => AsExpression (). Compile ()( candidate ); }public abstract class CompositeSpecification < T > : ISpecification < T > { public abstract bool IsSatisfiedBy ( T candidate ); public ISpecification < T > And ( ISpecification < T > other ) => new AndSpecification < T > ( this , other ); public ISpecification < T > AndNot ( ISpecification < T > other ) => new AndNotSpecification < T > ( this , other ); public ISpecification < T > Or ( ISpecification < T > other ) => new OrSpecification < T > ( this , other ); public ISpecification < T > OrNot ( ISpecification < T > other ) => new OrNotSpecification < T > ( this , other ); public ISpecification < T > Not () => new NotSpecification < T > ( this ); }public class AndSpecification < T > : CompositeSpecification < T > { private ISpecification < T > _left ; private ISpecification < T > _right ;public AndSpecification ( ISpecification < T > left , ISpecification < T > right ) { _left = left ; _right = right ; }public override bool IsSatisfiedBy ( T candidate ) = > _left.IsSatisfiedBy ( candidate ) && _right.IsSatisfiedBy ( candidate ) ; }public class AndNotSpecification < T > : CompositeSpecification < T > { private ISpecification < T > _left ; private ISpecification < T > _right ;public AndNotSpecification ( ISpecification < T > left , ISpecification < T > right ) { _left = left ; _right = right ; }public override bool IsSatisfiedBy ( T candidate ) => _left . IsSatisfiedBy ( candidate ) && ! _right . IsSatisfiedBy ( candidate ); }public class OrSpecification < T > : CompositeSpecification < T > { private ISpecification < T > _left ; private ISpecification < T > _right ;public OrSpecification ( ISpecification < T > left , ISpecification < T > right ) { _left = left ; _right = right ; }public override bool IsSatisfiedBy ( T candidate ) => _left . IsSatisfiedBy ( candidate ) || _right . IsSatisfiedBy ( candidate ); } public class OrNotSpecification < T > : CompositeSpecification < T > { private ISpecification < T > _left ; private ISpecification < T > _right ;public OrNotSpecification ( ISpecification < T > left , ISpecification < T > right ) { _left = left ; _right = right ; }public override bool IsSatisfiedBy ( T candidate ) = > _left.IsSatisfiedBy ( candidate ) || ! _right.IsSatisfiedBy ( candidate ) ; }public class NotSpecification < T > : CompositeSpecification < T > { ISpecification < T > other ; public NotSpecification ( ISpecification < T > other ) => this . other = other ; public override bool IsSatisfiedBy ( T candidate ) => ! other . IsSatisfiedBy ( candidate ); }from abc import ABC , abstractmethod from dataclasses import dataclass from typing import Anyclass BaseSpecification ( ABC ): @abstractmethod def is_satisfied_by ( self , candidate : Any ) -> bool : raise NotImplementedError ()def __call__ ( self , candidate : Any ) -> bool : return self . is_satisfied_by ( candidate )def __and__ ( self , other : "BaseSpecification" ) -> "AndSpecification" : return AndSpecification ( self , other )def __or__ ( self , other : "BaseSpecification" ) -> "OrSpecification" : return OrSpecification ( self , other )def __neg__ ( self ) -> "NotSpecification" : return NotSpecification ( self )@dataclass ( frozen = True ) class AndSpecification ( BaseSpecification ): first : BaseSpecification second : BaseSpecificationdef is_satisfied_by ( self , candidate : Any ) -> bool : return self . first . is_satisfied_by ( candidate ) and self . second . is_satisfied_by ( candidate )@dataclass ( frozen = True ) class OrSpecification ( BaseSpecification ): first : BaseSpecification second : BaseSpecificationdef is_satisfied_by ( self , candidate : Any ) - > bool : return self.first.is_satisfied_by ( candidate ) or self.second.is_satisfied_by ( candidate )@dataclass ( frozen = True ) class NotSpecification ( BaseSpecification ): subject : BaseSpecificationdef is_satisfied_by ( self , candidate : Any ) - > bool : return not self.subject.is_satisfied_by ( candidate )template < class T > class ISpecification { public : virtual ~ ISpecification () = default ; virtual bool IsSatisfiedBy ( T Candidate ) const = 0 ; virtual ISpecification < T >* And ( const ISpecification < T >& Other ) const = 0 ; virtual ISpecification < T >* AndNot ( const ISpecification < T >& Other ) const = 0 ; virtual ISpecification < T >* Or ( const ISpecification < T >& Other ) const = 0 ; virtual ISpecification < T >* OrNot ( const ISpecification < T >& Other ) const = 0 ; virtual ISpecification < T >* Not () const = 0 ; };template < class T > class CompositeSpecification : public ISpecification < T > { public : virtual bool IsSatisfiedBy ( T Candidate ) const override = 0 ;virtual ISpecification < T >* And ( const ISpecification < T >& Other ) const override ; virtual ISpecification < T >* AndNot ( const ISpecification < T >& Other ) const override ; virtual ISpecification < T >* Or ( const ISpecification < T >& Other ) const override ; virtual ISpecification < T >* OrNot ( const ISpecification < T >& Other ) const override ; virtual ISpecification < T >* Not () const override ; };template < class T > class AndSpecification final : public CompositeSpecification < T > { public : const ISpecification < T >& Left ; const ISpecification < T >& Right ;AndSpecification ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Left ( InLeft ), Right ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( Candidate ) && Right . IsSatisfiedBy ( Candidate ); } };template < class T > ISpecification < T >* CompositeSpecification < T >:: And ( const ISpecification < T >& Other ) const { return new AndSpecification < T > ( * this , Other ); }template < class T > class AndNotSpecification final : public CompositeSpecification < T > { public : const ISpecification < T >& Left ; const ISpecification < T >& Right ;AndNotSpecification ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Left ( InLeft ), Right ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( Candidate ) && ! Right . IsSatisfiedBy ( Candidate ); } };template < class T > class OrSpecification final : public CompositeSpecification < T > { public : const ISpecification < T >& Left ; const ISpecification < T >& Right ;OrSpecification ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Left ( InLeft ), Right ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( Candidate ) || Right . IsSatisfiedBy ( Candidate ); } };template < class T > class OrNotSpecification final : public CompositeSpecification < T > { public : const ISpecification < T >& Left ; const ISpecification < T >& Right ;OrNotSpecification ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Left ( InLeft ), Right ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( Candidate ) || ! Right . IsSatisfiedBy ( Candidate ); } };template < class T > class NotSpecification final : public CompositeSpecification < T > { public : const ISpecification < T >& Other ;NotSpecification ( const ISpecification < T >& InOther ) : Other ( InOther ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return ! Other . IsSatisfiedBy ( Candidate ); } };template < class T > ISpecification < T >* CompositeSpecification < T >:: AndNot ( const ISpecification < T >& Other ) const { return new AndNotSpecification < T > ( * this , Other ); }template < class T > ISpecification < T >* CompositeSpecification < T >:: Or ( const ISpecification < T >& Other ) const { return new OrSpecification < T > ( * this , Other ); }template < class T > ISpecification < T >* CompositeSpecification < T >:: OrNot ( const ISpecification < T >& Other ) const { return new OrNotSpecification < T > ( * this , Other ); }template < class T > ISpecification < T >* CompositeSpecification < T >:: Not () const { return new NotSpecification < T > ( * this ); }export interface ISpecification { isSatisfiedBy ( candidate : unknown ) : boolean ; and ( other : ISpecification ) : ISpecification ; andNot ( other : ISpecification ) : ISpecification ; or ( other : ISpecification ) : ISpecification ; orNot ( other : ISpecification ) : ISpecification ; not () : ISpecification ; }export abstract class CompositeSpecification implements ISpecification { abstract isSatisfiedBy ( candidate : unknown ) : boolean ;and ( other : ISpecification ) : ISpecification { return new AndSpecification ( this , other ); }andNot ( other : ISpecification ) : ISpecification { return new AndNotSpecification ( this , other ); }または( other : ISpecification ) : ISpecification { return new OrSpecification ( this , other ); }orNot ( other : ISpecification ) : ISpecification { return new OrNotSpecification ( this , other ); }not () : ISpecification { return new NotSpecification ( this ); } }export class AndSpecification extends CompositeSpecification { constructor ( private leftCondition : ISpecification , private rightCondition : ISpecification ) { super (); }isSatisfiedBy (候補:不明) : boolean {これを返します。左の状態。isSatisfiedBy (候補) && this .右の状態。isSatisfiedBy (候補者); } }export class AndNotSpecification extends CompositeSpecification { constructor ( private leftCondition : ISpecification , private rightCondition : ISpecification ) { super (); }isSatisfiedBy (候補:不明) : boolean {これを返します。左の状態。isSatisfiedBy (候補) && this .右の状態。isSatisfiedBy (候補) !== true ; } }export class OrSpecification extends CompositeSpecification { constructor ( private leftCondition : ISpecification , private rightCondition : ISpecification ) { super (); }isSatisfiedBy (候補:不明) : boolean {これを返します。左の状態。isSatisfiedBy (候補者) ||これ。右の状態。isSatisfiedBy (候補者); } }export class OrNotSpecification extends CompositeSpecification { constructor ( private leftCondition : ISpecification , private rightCondition : ISpecification ) { super (); }isSatisfiedBy (候補:不明) : boolean {これを返します。左の状態。isSatisfiedBy (候補者) ||これ。右の状態。isSatisfiedBy (候補) !== true ; } }export class NotSpecification extends CompositeSpecification { constructor ( private wrapped : ISpecification ) { super (); }isSatisfiedBy ( candidate : unknown ) : boolean { return ! this . wrapped . isSatisfiedBy ( candidate ); } }次の例では、以下の条件を満たす場合に請求書が取得され、債権回収業者に送付されます。
この例は、ロジックがどのように「連鎖」されるかの結果を示すためのものです。
この使用例では、OverdueSpecification請求書の支払期日が30日以上経過した場合に満たされるクラス、NoticeSentSpecification顧客に3通の通知が送付された場合に満たされるクラス、およびInCollectionSpecification請求書が既に債権回収業者に送付された場合に満たされるクラスが、事前に定義されていることを前提としています。これらのクラスの実装方法はここでは重要ではありません。
これら 3 つの仕様を使用して、請求書の支払いが遅延している場合、顧客に通知が送信されている場合、およびまだ債権回収業者に渡っていない場合に満たされる新しい仕様を作成SendToCollectionしました。
var overdue = new OverdueSpecification (); var noticeSent = new NoticeSentSpecification (); var inCollection = new InCollectionSpecification ();// 仕様パターンのロジックチェーンの例var sendToCollection = overdue . And ( noticeSent ). And ( inCollection . Not ());var invoices = InvoiceService.GetInvoices ( ) ;foreach ( var invoice in invoices ) { if ( sendToCollection.IsSatisfiedBy ( invoice ) ) { invoice.SendToCollection ( ) ; } }