7

我正在设计一个具有简单实体框架支持的域对象的系统,该对象具有我需要根据一系列规则更新的字段 - 我想逐步实现这些规则(以敏捷风格)并且当我使用 EF 时我持怀疑态度关于将每个规则放入域对象中。但是,我想避免编写“程序代码”和使用贫血的域模型。这一切都需要可测试。

例如,对象是:

class Employee { 
 private string Name; 
 private float Salary; 
 private float PensionPot;
 private bool _pension;
 private bool _eligibleForPension;

}

我需要建立一些规则,例如“如果 Salary 高于 100,000 并且 _eligibleForPension 为 false,则将 _eligibleForPension 设置为 true”和“如果 _pension 为 true,则将 _eligibleForPension 设置为 true”。

大约有 20 条这样的规则,我正在寻找是否应该在 Employee 类或类似 EmployeeRules 类中实现它们的建议?我的第一个想法是为从“Rule”继承的每个规则创建一个单独的类,然后将每个规则应用于 Employee 类,可能使用访问者模式,但我必须将所有字段暴露给规则才能做到这一点感觉不对。虽然在 Employee 类上设置每条规则也感觉不太正确。这将如何实施?

第二个问题是实际的员工是支持数据库的实体框架实体,所以我不乐意为这些“实体”添加逻辑 - 特别是当我需要模拟对象以对每个规则进行单元测试时。如果他们有我在同一个对象上测试的规则,我怎么能嘲笑他们?

我一直在考虑在应用规则之前使用 AutoMapper 转换为更简单的域对象,但随后需要自己管理对字段的更新。对此也有什么建议吗?

4

2 回答 2

7

一种方法是使规则成为Employee. 这种方法的好处是字段可以保持私有。此外,规则的调用可以由 Employee 类本身强制执行,确保它们总是在需要时被调用:

class Employee
{
    string id;
    string name;
    float salary;
    float pensionPot;
    bool pension;
    bool eligibleForPension;

    public void ChangeSalary(float salary)
    {
        this.salary = salary;
        ApplyRules();
    }

    public void MakeEligibleForPension()
    {
        this.eligibleForPension = true;
        ApplyRules(); // may or may not be needed
    }

    void ApplyRules()
    {
        rules.ForEach(rule => rule.Apply(this));
    }

    readonly static List<IEmployeeRule> rules;

    static Employee()
    {
        rules = new List<IEmployeeRule>
        {
            new SalaryBasedPensionEligibilityRule()
        };
    }

    interface IEmployeeRule
    {
        void Apply(Employee employee);
    }

    class SalaryBasedPensionEligibilityRule : IEmployeeRule
    {
        public void Apply(Employee employee)
        {
            if (employee.salary > 100000 && !employee.eligibleForPension)
            {
                employee.MakeEligibleForPension();
            }
        }
    }
}

这里的一个问题是 Employee 类必须包含所有的规则实现。这不是一个主要问题,因为这些规则体现了与员工养老金相关的业务逻辑,因此它们确实属于同一类。

于 2013-03-26T20:20:06.437 回答
4

业务规则通常是一个有趣的话题。聚合/实体不变量和业务规则之间肯定存在差异。业务规则可能需要外部数据,我不同意更改聚合/实体的规则。

您应该考虑规则的规范模式。该规则应该基本上只返回它是否被破坏,并可能带有某种描述。

在您的示例SalaryBasedPensionEligibilityRule中,如 eulerfx 使用的那样,可能需要一些PensionThreshold. 该规则确实看起来更像是一项任务,因为该规则实际上并未检查实体的任何有效性。

所以我建议规则是一种决策机制,任务是为了改变状态。

话虽如此,您可能想在这里向实体寻求建议,因为您可能不想公开状态:

public class Employee
{
    float salary;
    bool eligibleForPension;

    public bool QualifiesForPension(float pensionThreshold) 
    {
        return salary > pensionThreshold && !eligibleForPension;
    }

    public void MakeEligibleForPension()
    {
        eligibleForPension = true;
    }
}

这符合命令/查询分离的想法。

如果您直接从您的 ORM 对象构建并且不想或不能包含所有行为,那没关系 --- 但它肯定会有所帮助:)

于 2013-03-27T05:20:25.753 回答