以下是其他解决方案的规则设计,你可以参考: 什么是以下规则设计的其他解决方案?

7
我希望为某些对象类创建一个简单的验证系统,基本上
public interface IMyClassRule {
    bool IsValid(MyClass obj, Context someAdditionalData)
}

规则列表是通过 DI 框架自动发现的,而不是提前固定的。
比如说,我有一个 VeryGeneralDefaultRuleAboutAllObjects : IMyClassRule 和一个 SpecificCaseWhenGeneralRuleDoesNotApply : IMyClassRule。在通用方式下如何处理这个解决方案(基本上允许在某些情况下任何规则被任何其他规则覆盖)?
我考虑过以下几种解决方法:
  1. 规则或规则结果的数字优先级

    优点:简单易懂易于实现。
    缺点:我需要知道/猜测原始规则的优先级。不明确哪个优先级是第一位(1 还是 1000)?需要一些“不关心”的规则结果来处理特殊情况。

  2. 基于类型的优先级(基本上是 .Before<VeryGeneralRule>

    优点:明确声明你想要达到的目标。
    缺点:需要显式引用原始规则。排序逻辑会很复杂。需要一些“不关心”的规则结果来处理特殊情况。

是否还有其他更好的选择呢?

1
非常烦人的问题。我已经为此争论了数小时,但仍未得出令人满意的解决方案。 - CodesInChaos
2
考虑放弃自动发现,手动创建按优先级排序的中央规则列表。 - CodesInChaos
看看Web规则的概念(http://rule.codeeffects.com)。我认为你正在寻找类似于他们所做的东西。 - Kizz
我同意CodeInChaos的观点。在我参与的一个项目中,我们使用工厂来创建外观相似的规则。所创建的规则集取决于它们被创建的上下文环境。 - Andrew Kennan
2个回答

2
我认为这在很大程度上取决于项目的范围以及你需要多么松散地耦合。我做了很多与业务规则相关的工作,它们需要尽可能具有可扩展性。如果规则数量稍微有点大,或者它们的排序甚至稍微复杂一些,我不会将自己绑定在序数规则系统上。我认为自动发现/连接规则绝对是此处的最佳选择。
在我看来,这种情况的关键是,一般情况下的规则并不是由与其范围相关的逻辑缺失所定义的。一般情况下的规则必须具有与特定情况下的规则一样具体的范围逻辑。它们可能在100次中有99次在范围内,但它们仍然需要具有具体的范围逻辑。
以下大致是我的处理方法。我不太喜欢将WithinScope()直接附加到IRule,但考虑到您正在考虑一个序数列表,我假设该逻辑要么是可管理且相对静态的,要么可以注入一个委托来处理该逻辑。 框架接口
public interface IRule<in T>{
    bool IsValid(T obj);
    bool WithinScope();
}

public interface IValidator<in T>{
    bool IsValid(T obj);
}

public interface IRuleFactory<in T>{
    IEnumerable<IRule<T>> BuildRules();
}

通用验证器和规则工厂

public class GenericValidator<T> : IValidator<T>
{
    private readonly IEnumerable<IRule<T>> _rules;

    public GenericValidator(IRuleFactory<T> ruleFactory){
        _rules = ruleFactory.BuildRules();
    }

    public bool IsValid(T obj){
        return _rules.All(p => p.IsValid(obj));
    }
}

public class GenericRuleFactory<T> : IRuleFactory<T>
{
    private readonly IEnumerable<IRule<T>> _rules;

    public GenericRuleFactory(IEnumerable<IRule<T>> rules){
        _rules = rules;
    }

    public IEnumerable<IRule<T>> BuildRules(){
        return _rules.Where(x => x.WithinScope());
    }
}

示例规则

public class VeryGeneralDefaultRuleAboutAllObjects : IRule<IMyClass>
{
    private readonly Context _context;

    public VeryGeneralDefaultRuleAboutAllObjects(Context context){
        _context = context;    
    }

    public bool IsValid(IMyClass obj){
        return !obj.IsAllJackedUp;
    }

    public bool WithinScope(){
        return !_context.IsSpecialCase;
    }
}

public class SpecificCaseWhenGeneralRuleDoesNotApply : IRule<IMyClass>
{
    private readonly Context _context;

    public VeryGeneralDefaultRuleAboutAllObjects(Context context){
        _context = context;    
    }

    public bool IsValid(IMyClass obj){
        return !obj.IsAllJackedUp && _context.HasMoreCowbell;
    }

    public bool WithinScope(){
        return _context.IsSpecialCase;
    }
}

IoC装配(使用StructureMap)

public static class StructureMapBootstrapper
{
    public static void Initialize()
    {
        ObjectFactory.Initialize(x =>
        {
            x.Scan(scan =>
            {
                scan.TheCallingAssembly();
                scan.AssembliesFromApplicationBaseDirectory();
                scan.AddAllTypesOf(typeof (IRule<>));
            });

            x.For(typeof(IValidator<>))
                .Use(typeof(GenericValidator<>));

            x.For(typeof(IRuleFactory<>))
                .Use(typeof(GenericRuleFactory<>));
        });
    }
}

我最近实现了一个看起来非常像这个的东西,只是没有(更)复杂的StructureMap连接。我喜欢这个答案,因为它证明了我所做的事情至少被社区中的另一个人视为合理。 - Tom W

0

如何在规则实例中添加注入一些条件的能力,例如,IRuleApplicability接口的实例。您可以将其与类似于#2的内容结合使用,并使用所有规则的基类在应用规则之前检查适用性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接