DDD中的Specification模式
2011-09-12 16:44 Henry Cui 阅读(4673) 评论(7) 编辑 收藏 举报在领域驱动开发中,我们会常用到Repository、Unit of Work等模式,而Specification模式并不是很常用,Specification模式在领域层中主要为我们实现领域规则的自由组合。
关于Specification
首先我们来看常见的Specification模式中的类图(来自于http://en.wikipedia.org/wiki/Specification_pattern):
Specification,规格说明书,这里我们可以理解为规则约束,我们可以对每个规则定义一个Specification,同时也可以将不同的Specification进行组合使用。
简单实现
首先我们定义ISpecification接口:
public interface ISpecification<T> { bool IsSatisfiedBy(T candidate); }
我们定义ICompsiteSpecification,这里使用了Compsite模式(组合模式)
public interface ICompsiteSpecification<T>:ISpecification<T> { ISpecification<T> Add(ISpecification<T> other); ISpecification<T> Or(ISpecification<T> other); ISpecification<T> Not(); }
AddSpecification:
public class AddSpecification<T>:ISpecification<T> { private ISpecification<T> _Left; private ISpecification<T> _Right; public AddSpecification(ISpecification<T> left,ISpecification<T> right) { _Left = left; _Right = right; } public bool IsSatisfiedBy(T candidate) { return _Left.IsSatisfiedBy(candidate) && _Right.IsSatisfiedBy(candidate); } }
OrSpecification:
public class OrSpecification<T>:ISpecification<T> { private ISpecification<T> _Left; private ISpecification<T> _Right; public OrSpecification(ISpecification<T> left, ISpecification<T> right) { _Left = left; _Right = right; } public bool IsSatisfiedBy(T candidate) { return _Left.IsSatisfiedBy(candidate) || _Right.IsSatisfiedBy(candidate); } }
NoSpecification:
public class NotSpecification<T>:ISpecification<T> { private ISpecification<T> _Wrapped; public NotSpecification(ISpecification<T> wrapped) { _Wrapped = wrapped; } public bool IsSatisfiedBy(T candidate) { return (!_Wrapped.IsSatisfiedBy(candidate)); } }
CompositeSpecification:
public abstract class CompositeSpecification<T>:ICompsiteSpecification<T> { public abstract bool IsSatisfiedBy(T candidate); public ISpecification<T> Add(ISpecification<T> other) { return new AddSpecification<T>(this, other); } public ISpecification<T> Or(ISpecification<T> other) { return new OrSpecification<T>(this, other); } public ISpecification<T> Not() { return new NotSpecification<T>(this); } }
我们举了个实际的例子进行使用,比如我们要获取一个数组里面所有的大于0而且是偶数的数字。这个时候我们可以定义两个Specification,一个是整数的Specification,一个是偶数的Specification:
public class EvenSpecification:CompositeSpecification<int> { public override bool IsSatisfiedBy(int candidate) { return candidate % 2 == 0; } }
我们再定义一个判断为正数的Specification:
public class PositiveSpecification:CompositeSpecification<int> { public override bool IsSatisfiedBy(int candidate) { return candidate > 0; } }
使用:
static void Main(string[] args) { var numbers = Enumerable.Range(-10,20); var postivieSpecification = new PositiveSpecification(); var compositSpecification = postivieSpecification.Add(new EvenSpecification()); foreach (var item in numbers.Where(i => compositSpecification.IsSatisfiedBy(i))) { Console.WriteLine(item); } Console.ReadKey(); }
DDD中使用
举个在我们实际业务中的查询来说明下吧。在.net 3.0之后我们有了Lambda表达式,我们可以通过更为优雅的一种方式来实现了:
public interface ISpecification<T> { Expression<Func<T, bool>> IsSatisfiedBy(); }
在这里IsSatisfiedBy方法返回的不是bool类型的,而是返回一个Lambda表达式,是为了跟Where进行配合使用。
这个时候我们对Expression<Func<T,bool>>类型进行扩展增加其And、Or方法:
public static class ExpressionBuilder { public static Expression<T> Compose<T>(this Expression<T> left,Expression<T> right,Func<Expression, Expression, Expression> merge) { var params1 = left.Parameters; var params2 = right.Parameters; var map = params1.Select((p,i)=> new {p,s= params2[i]}).ToDictionary(p=>p.s,p=>p.p); var rightBody = ParameterRebinder.ReplaceParameters(map,right.Body); return Expression.Lambda<T>(merge(left, rightBody), left.Parameters); } public static Expression<Func<T,bool>> And<T>(this Expression<Func<T,bool>> left,Expression<Func<T,bool>> right) { return left.Compose(right, Expression.Add); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right) { return left.Compose(right, Expression.Or); } }
Visitor:
public class ParameterRebinder:ExpressionVisitor { private readonly Dictionary<ParameterExpression, ParameterExpression> _Map; public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) { _Map = map; } protected override Expression VisitParameter(ParameterExpression node) { ParameterExpression replacement; if (_Map.TryGetValue(node,out replacement)) { node = replacement; } return base.VisitParameter(node); } public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) { return (new ParameterRebinder(map)).Visit(exp); } }
上面的扩展为了我们方便的实现Expression<Func<T,bool>>类型进行我们逻辑And、Or操作。对于表达式书不太熟悉的同学可以参考MSDN上面的两篇文章:http://msdn.microsoft.com/zh-cn/library/bb397951.aspx、http://msdn.microsoft.com/zh-cn/library/bb546136.aspx
为了方便我们进行And、Or动作,我们可以对操作符进行重载:
public abstract class Specification<T>:ISpecification<T> { public abstract Expression<Func<T, bool>> IsSatisfiedBy(); public static ISpecification<T> operator |(Specification<T> left, Specification<T> right) { return new OrSpecification<T>(left, right); } public static ISpecification<T> operator &(Specification<T> left, Specification<T> right) { return new AddSpecification<T>(left, right); } public static ISpecification<T> operator !(Specification<T> specification) { return new NotSpecification<T>(specification); } public static bool operator false(Specification<T> specification) { return false; } public static bool operator true(Specification<T> specification) { return true; } }
CompositeSpecification:
public abstract class CompositeSpecification<T>:Specification<T> { public abstract ISpecification<T> Left { get; } public abstract ISpecification<T> Right { get; } }
And:
public class AddSpecification<T>:CompositeSpecification<T> { private ISpecification<T> _Left; private ISpecification<T> _Right; public AddSpecification(ISpecification<T> left,ISpecification<T> right) { _Left = left; _Right = right; } public override ISpecification<T> Left { get { return _Left; } } public override ISpecification<T> Right { get { return _Right; } } public override Expression<Func<T,bool>> IsSatisfiedBy() { return _Left.IsSatisfiedBy().And(_Right.IsSatisfiedBy()); } }
Or:
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 ISpecification<T> Left { get { return _Left; } } public override ISpecification<T> Right { get { return _Right; } } public override Expression<Func<T, bool>> IsSatisfiedBy() { return _Left.IsSatisfiedBy().Or(_Right.IsSatisfiedBy()); } }
Not:
public class NotSpecification<T>:Specification<T> { private ISpecification<T> _Wrapped; public NotSpecification(ISpecification<T> wrapped) { _Wrapped = wrapped; } public override Expression<Func<T,bool>> IsSatisfiedBy() { return Expression.Lambda<Func<T, bool>>(Expression.Not(_Wrapped.IsSatisfiedBy().Body), _Wrapped.IsSatisfiedBy().Parameters.Single()); } }
其实我们上面的实现的类图是这样的一种结构:
举个例子,比如我们在实际的查询中,可以定义自己的Specification,同时可以通过And、Or进行查询规则的组合,然后传递给Repository.
总结
本文中主要讲解了Specification模式,以及我们在项目中如何使用Specification的。希望对您有用。