惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
WordPress大学
WordPress大学
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
罗磊的独立博客
B
Blog
博客园_首页
A
About on SuperTechFans
有赞技术团队
有赞技术团队
V
V2EX
U
Unit 42
I
InfoQ
IT之家
IT之家
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
The Cloudflare Blog
H
Help Net Security

博客园 - Henry Cui

招聘高级.Net工程师 Node.js Web开发(二)认识Express(上) Node.js Web开发(一)从零开始 Html5 Step by Step(二) 本地存储 HTML 5 Step by Step(一) 拖放API Git学习笔记(一)初识Git Silverlight 4之旅(三)数据绑定(中) Silverlight 4之旅(二)数据绑定(上) Silverlight 4之旅(一) .Net 4.0 Parallel 编程之旅 .Net 4.0 Parallel 编程(九)Task中的数据共享(下) .Net 4.0 Parallel编程(八)Task中的数据共享(中) .Net 4.0 Parallel 编程(七)Task中的数据共享(上) .Net 4.0 Parallel 编程(六)Task(下) .Net 4.0 Parallel 编程(五)Task(中) .Net 4.0 Parallel 编程(四) Task(上) Entity Framework Code-First(下) Entity Framework Code-First(上) Entity Framework POCO T4模板使用
DDD中的Specification模式
Henry Cui · 2011-09-12 · via 博客园 - Henry Cui

在领域驱动开发中,我们会常用到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的。希望对您有用。