2017-03-01 11 views
0

私のプロジェクトでは、私は、以下に示す特定のパターンクラスを含んでいます。私はこれを実装する方法を知りません。これらのコードは以前の開発者に含まれています。仕様パターンの実装方法は?

public interface ISpecification<T> 
{ 
    Expression<Func<T, bool>> SpecExpression { get; } 
    bool IsSatisfiedBy(T obj); 
} 

public static class IExtensions 
{ 
    public static ISpecification<T> And<T>(
     this ISpecification<T> left, 
     ISpecification<T> right) 
    { 
     return new And<T>(left, right); 
    } 

    public static ISpecification<T> Or<T>(
     this ISpecification<T> left, 
     ISpecification<T> right) 
    { 
     return new Or<T>(left, right); 
    } 

    public static ISpecification<T> Negate<T>(this ISpecification<T> inner) 
    { 
     return new Negated<T>(inner); 
    } 
} 

public abstract class SpecificationBase<T> : ISpecification<T> 
{ 
    private Func<T, bool> _compiledExpression; 

    private Func<T, bool> CompiledExpression 
    { 
     get { return _compiledExpression ?? (_compiledExpression = SpecExpression.Compile()); } 
    } 

    public abstract Expression<Func<T, bool>> SpecExpression { get; } 

    public bool IsSatisfiedBy(T obj) 
    { 
     return CompiledExpression(obj); 
    } 
} 

public class And<T> : SpecificationBase<T> 
{ 
    ISpecification<T> left; 
    ISpecification<T> right; 

    public And(
     ISpecification<T> left, 
     ISpecification<T> right) 
    { 
     this.left = left; 
     this.right = right; 
    } 

    // AndSpecification 
    public override Expression<Func<T, bool>> SpecExpression 
    { 
     get 
     { 
      var objParam = Expression.Parameter(typeof(T), "obj"); 

      var newExpr = Expression.Lambda<Func<T, bool>>(
       Expression.AndAlso(
        Expression.Invoke(left.SpecExpression, objParam), 
        Expression.Invoke(right.SpecExpression, objParam) 
       ), 
       objParam 
      ); 

      return newExpr; 
     } 
    } 
} 

public class Or<T> : SpecificationBase<T> 
{ 
    ISpecification<T> left; 
    ISpecification<T> right; 

    public Or(
     ISpecification<T> left, 
     ISpecification<T> right) 
    { 
     this.left = left; 
     this.right = right; 
    } 

    // OrSpecification 
    public override Expression<Func<T, bool>> SpecExpression 
    { 
     get 
     { 
      var objParam = Expression.Parameter(typeof(T), "obj"); 

      var newExpr = Expression.Lambda<Func<T, bool>>(
       Expression.OrElse(
        Expression.Invoke(left.SpecExpression, objParam), 
        Expression.Invoke(right.SpecExpression, objParam) 
       ), 
       objParam 
      ); 

      return newExpr; 
     } 
    } 
} 

public class Negated<T> : SpecificationBase<T> 
{ 
    private readonly ISpecification<T> _inner; 

    public Negated(ISpecification<T> inner) 
    { 
     _inner = inner; 
    } 

    // NegatedSpecification 
    public override Expression<Func<T, bool>> SpecExpression 
    { 
     get 
     { 
      var objParam = Expression.Parameter(typeof(T), "obj"); 

      var newExpr = Expression.Lambda<Func<T, bool>>(
       Expression.Not(
        Expression.Invoke(this._inner.SpecExpression, objParam) 
       ), 
       objParam 
      ); 

      return newExpr; 
     } 
    } 
} 

簡単な例で上記の仕様を実装する方法は?この仕様の使用は何ですか?

+0

これは、「式」の助けを借りて実装された仕様パターンです。そのユースケースは、ドメインモデルによって異なります。 –

+0

@Ofir Winegarten上記のクラスで小さな例を挙げられますか?それの使い方? – Pradees

答えて

0

コメントに書いたとおり、これはExpressionの助けを借りて実装された仕様パターンです。

のは、我々は、以下のドメインモデルを持っているとしましょう:

public class Person 
{ 
    public string Name { get; set; } 
    public DateTime BirthDate { get; set; } 
    public string Country { get; set; } 
} 

そして、また、私たちはこれらのリストを持っている:

List<Person> persons; // <-- initialized elsewhere 

今、我々は彼らのための2つの仕様を持つことができます。あなたがそれらをofcourseのチェーンにすることができます

ISpecification spec = new SpainSpec(); 
persons.Where (spec.IsSatisfiedBy); 

01/01/2000

public class SpainSpec : SpecificationBase<Person> 
{ 
    public override Expression<Func<Person, bool>> SpecExpression => person => person.Country == "Spain"; 
} 

public class BornBefore2000 : SpecificationBase<Person> 
{ 
    public override Expression<Func<Person, bool>> SpecExpression => person => person.BirthDate < DateTime.Parse("2000-01-01"); 
} 

前に生まれた者今、私たちは2000年以前に生まれたすべての人を見つけるためにそれを使用することができますためSpainに住んでいる人のための1つずつを持っていることができます2000年以前に生まれたことをスペインからのものを得る:

ISpecification spec = new SpainSpec().And(new BornBefore2000()); 
persons.Where (spec.IsSatisfiedBy); 

これは本当にあなたがあなたのモデルやニーズに応じて、より多くのを持つことができ、非常に単純なシナリオです。

仕様を使用する際には、コントロールを緩めたり、クラスが多すぎたり、ホイールを再発明したりしないように注意してください。

関連する問題