代码改变世界

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_UML

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.aspxhttp://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());
    }
}

其实我们上面的实现的类图是这样的一种结构:

image

举个例子,比如我们在实际的查询中,可以定义自己的Specification,同时可以通过And、Or进行查询规则的组合,然后传递给Repository.

总结

本文中主要讲解了Specification模式,以及我们在项目中如何使用Specification的。希望对您有用。

作者:Henllyee Cui
出处: http://henllyee.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。