规格图案:多种类型复合材料规格

时间:2014-10-18 12:27:20

标签: domain-driven-design specifications

我需要复杂的多种类型<TLeft,TRight>)规范赞:

public class AndSopecification<TLeft,TRight>
{

    public AndSpecification(ISpecification<TLeft> leftSide, ISpecification<TRight> rightSide)
    {
    }
    .
    .
    .
}

但我看到的所有规范示例都是实现单一类型<T>),如:

public sealed class AndSpecification<T> : CompositeSpecification<T>
{
    public AndSpecification(ISpecification<T> leftSide, ISpecification<T> rightSide)
    {            
    }
    .
    .
    .
}

如果我创建如下的复杂规范,是否有任何问题:

new AndSpecification<Foo,Bar>(new FooSpecification(),new BarSpecification()).SatisfiedBy();

因此,我可以在不同情况下重复使用所有规范,然后我可以拥有单个唯一的过程点,这些过程点可以是更复杂的规范树的组合单元。

但我找不到那样的实现。

我是在正确的道路上吗?

修改

我正在将客户使用的每个条件句翻译成specification并在客户重用句子的任何地方重复使用

(在每个相关的logicquery predicatebusiness rule validation,...中,我认为这些specifications在这些情况中自然是相同的)

正如客户所做的那样,我创建了composite specifications

但我在每种情况下都使用不同的基础架构访问它们(query predicatebusiness rule validation,...)

3 个答案:

答案 0 :(得分:1)

我有一个实现,我可以执行以下操作,这使我能够重用我认为合适的规范。

public class Composite
{
  public Foo _foo;
  public Bar _bar;
}

var fooSpec = new Specification<Foo>(f => f.IsActive);
var barSpec = new Specification<Bar>(f => f.IsActive);

var comSpec = new Specification<TheComposite>(c => true);

comSpec = comSpec.And(c => c.Foo, fooSpec)
                 .And(c => c.Bar, barSpec); 

如果这是您正在寻找的,我就是这样做的。

public class Specification<TEntity>
{
    private readonly Expression<Func<TEntity, bool>> _expression;

    public static Specification<TEntity> Create<TNestedEntity>(Expression<Func<TEntity, TNestedEntity>> propertyExpression, ISpecification<TNestedEntity> spec)
    {
        var replacer = new ParameterReplaceVisitor(spec.EvalExpression.Parameters.First(), propertyExpression.Body);
        var newExpression = replacer.Visit(spec.EvalExpression.Body);
        var exp = Expression.Lambda<Func<TEntity, bool>>(newExpression, propertyExpression.Parameters);
        return new Specification<TEntity>(exp);
    }

    public virtual bool IsSatisfiedBy(TEntity e)
    {
        return EvalFunc(e);
    }

    public ISpecification<TEntity> And(ISpecification<TEntity> other)
    {
        return new Specification<TEntity>(EvalExpression.And(other.EvalExpression));
    }

    public ISpecification<TEntity> And<TNestedEntity>(Expression<Func<TEntity, TNestedEntity>> property, ISpecification<TNestedEntity> spec)
    { 
        return And(Create(property, spec));
    }
    ...
}

private class ParameterReplaceVisitor : ExpressionVisitor
{
    private readonly Expression _replacementExpression;
    private readonly ParameterExpression _parameter;

    public ParameterReplaceVisitor(ParameterExpression parameter, Expression replacementExpression)
    {
        _parameter = parameter;
        _replacementExpression = replacementExpression;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        // If the node is the parameter we are trying to replace,
        // return the expression that should replace it.
        return (node == _parameter) ? _replacementExpression : node;
    }
}

ExpressionVisitor是System.Linq.Expressions

的一部分

您当然也可以实现Or()

我希望这是你正在寻找的,这会有所帮助。

答案 1 :(得分:1)

创建与特定类型的对象无关的规范会破坏目的。来自Eric Evans:&#34; SPECIFICATION是一个谓词,用于确定某个对象是否满足某些条件。&#34;因此,您始终必须针对实例评估规范。复合规范用于组成与同一类对象相关的规范。

规范与业务规则不同。您可以使用多个规范的业务规则。例如:如果客户想购买一辆大型汽车,那么他需要在他的银行账户中存一些钱。

var bigCar = new Specification<Car>(car => car.Size == "big");
var accountHasMoney = new Specification<Account>(account => account.Balance > 0);

// This rule takes a car and an account
public bool CanBuyBigCar(Car car, Account account) {
  return bigCar.IsSatisfiedBy(car) && accountHasMoney.IsSatisfiedBy(account)
}

如果要在sql查询中重用这些规范,那么可以使用隐式运算符使其更容易与IQueryables一起使用:

public static implicit operator Expression<Func<T, bool>>(Specification<T> specification)
{
  return specification.Expression;
}

public static implicit operator Func<T, bool>(Specification<T> specification)
{
  return specification.IsSatisfiedBy;
}

现在所有IQueryables,IEnumerables和所有其他类型的集合都可以直接查询规范。

您可以在此处找到即用型实施:https://github.com/jnicolau/NSpecifications/tree/master/Nspecifications

答案 2 :(得分:1)

我很难理解为什么需要指定多个类型。但是没有理由您不能这样做。从技术上讲,您可以使用一个元组规范。

var spec = new Spec<(int num, string text)>(x => x.num == 1 && x.text == "a");