2017-03-01 7 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의 도움으로 구현 된 사양 패턴입니다.

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

이제 우리는 그 두 가지 사양을 가질 수 있습니다 또한, 우리는 이들의 목록을 가지고,

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

을 그리고 :

의 우리는 다음과 같은 도메인 모델을한다고 가정 해 봅시다. 당신은 그들을 당연히 체인 수

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에 사는 사람들 하나 하나가하자 2000 년 이전에 태어난 그 스페인에서 사람들을 얻을 :

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

이것은 당신이 모델과 필요에 따라, 많은 사람들이 더있을 수있는 매우 간단한 시나리오가 정말이다.

사양을 사용할 때 제어가 느슨해 지거나 클래스가 너무 많거나 휠체어를 다시 발명하지 않도록주의하십시오.