业务规则 - 他们在OOP中的位置?

时间:2009-06-04 08:31:11

标签: java validation oop business-logic

我有一个课程:安排。

public class Schedule {

private int locationNum;
private int cost;
private String costReason; 
private Date weekOfChange;
private Date dayOfChange;
private String changeReason; 

// and all those getters and setters

public Schedule(int locationNum, int cost, String costReason, Date weekOfChange, Date dayOfChange, String changeReason) throws ApplicationException {
//change is all or nothing - all attributes are present or none
if((weekOfChange!=null && dayOfChange!=null && changeReason!=null) || (weekOfChange==null  && dayOfChange == null && changeReason == null))  {
this.weekOfChange = weekOfChange;
this.dayOfChange = dayOfChange;
this.changeReason = changeReason;
}
else { throw new ApplicationException();}
//similary another if block to ensure that if cost is specified 
//then there exists the corresponding reason code for it.
}
}

到目前为止,我喜欢我的Schedule课程。但是,我没有完成检查,我必须做一些其他检查:

  • 是locationNum数据库中的有效商店编号。
  • 是changeReason text数据库中6个不同的changeReason代码之一。
  • 等等....

通常,我不会在Schedule类中写这些,显然,我不能从这个类中调用DAO。所以,我会有一个业务层和某种验证器类,接受一个类型为Schedule的对象,并按顺序执行一堆数据库验证,并收集显示/任何错误。

现在,这是我的问题:

  1. 如果您将Schedule视为POJO并认为对象不负责验证自身 - 我必须将构造函数中的所有代码移动到业务层的验证器类。但是,如果我这样做,是不是安排贫血?这是他们所谓的违反单一责任原则吗?
  2. 让我假设我将构造函数中的代码移动到业务层类中,以便各种验证现在都在我的业务层中。让我们假设有人在我的数据存储区中将dayOfChange更改为NULL,现在我正在从我的数据库加载对象。现在,有了这种对象,我的应用程序可以破解,不是吗?因为我会编写代码,假设验证规则得到满足。我想我的问题很混乱,但我想说的是,在这种情况下,我宁愿在构造函数中完成这些检查,以确保Schedule类隐藏的数据的完整性。
  3. 通常如何完成?什么是最佳做法?
  4. 感谢您参与此次讨论。

5 个答案:

答案 0 :(得分:5)

如果你真的拥有“所有那些getter setter”,那么你需要比在构造函数中更好地验证你的类。如果你的类的不变量是所有weekOfChange,dayOfChange和changeReason必须为null或者不是所有都必须不为null,那么你的setter很快就会把你的类置于无效状态。你的意思是要有制定者,还是说你的阶级是不可改变的?

在担心验证之前,应该对你的班级进行分析。查看给定状态的所有有效状态和不变量。然后,您将了解您的类是否应该是可变的或不可变的。如果你有相互依赖的共变量(比如weekOfChange,dayOfChannge和changeReason),将它们打包到自己的类中并使用Schedule类中的组合是有意义的。这会将关于这些事物字段的“规则”放在一个地方并简化时间表

与其他合作领域相同(如成本和成本原因)。然后,Schedule由自我验证类组成。如果这两个都是不可变的,并且Schedule也是不可变的,那么你的自我就更容易了。

所以,回答你的问题:一个类定义其状态和不变量更好,并且在实际可行的情况下仅向其协作者公开最小值。附表的内部状态的责任应该依赖于附表,并且只需稍加设计就可以相对容易地进行。

所以你有

    class Schedule {
         private Change change;
         private Cost cost;
         private Location location;

        public Schedule(Location location, Cost cost, Change change) {
           this.change = change;
           this.cost = cost;
           this.location = location;
        }
}

这样的colloborators
public class Change {
    private Date weekOfChange; //shoudln't these two fields be one, a date is a date after all??
    private Date dayOfChange; //shoudln't these two fields be one??
    private String changeReason; 

    public Change(Date weekOfChange Date dayOfChange, String changeReason) {
      this.weekOfChange = weekOfChange;
      ...etc.
    } 
}

虽然我强烈建议您通过防御性复制客户端代码传递的任何可变值来保护您的类不变量。

答案 1 :(得分:4)

  

如果您将Schedule视为一个   POJO并争辩说对象不是   负责验证自己 - 我   将不得不移动所有代码   业务的构造函数   图层的验证器类。但是,如果我   是这样做的,不是附表   贫血?这就是他们所说的   违反单一责任   原理

POJO仅表示您不必从特定类派生或使用字节代码增强器来获得OR映射等所需功能。这并不意味着对象应该是贫血而没有功能。

  

现在,有了这种对象,我的   申请可以破解,不是吗?   因为我会编写代码假设   验证规则得到满足。我猜   我的问题变得令人困惑,但是   我想说的是   在这种情况下,我宁愿拥有   这些检查在构造函数中完成   确保隐藏的数据的完整性   安排课程。

谁说你不能从Schedule类的构造函数中调用单独类中定义的规则?如果它需要访问非公共字段,您可以将它们打包为私有并将规则类放在同一个包中 - 或者将它们作为参数传递给规则。

  

通常如何完成?是什么   最佳实践?

是否将验证规则移至单独的类应取决于应用程序的需求,而不是误解的流行语。

好的有单独规则类的原因是:

  • 规则太多而且如此复杂,以至于将它们全部放入Schedule类会使它太大。
  • 您并不总是希望应用所有规则。
  • 可以重用这些规则来操作不一定在同一继承层次结构中的不同类(即它们通过接口工作)。

答案 2 :(得分:2)

Schedule构造函数看起来有两种截然不同的行为。因此,应该有两个构造函数。可能甚至是两个实现类。

locationNumchangeReason。这些目前是弱类型的。他们应该有他们自己的班级。根据您的问题,有效值取决于上下文。目前Schedule没有任何背景。您可以使Schedule与上下文无关,在这种情况下,locationNumchangeReason上没有数据库约束。换句话说,SchedulelocationNumchangeReason可能依赖于上下文,构造函数应该只检查它们是否都在同一个上下文中。包私有构造函数(充当穷人的friend)可能会省略检查。

POJO意味着(写得很好)的对象,因此也就是行为。问题似乎是用它来表示“结构”。避免使用结构。

业务层似乎具有误导性。它似乎是一个商业实体,它应该有商业行为。但是,它可能不包含业务流程(服务)。

您的应用程序将始终假设有关数据库的某些事项,包括(公共)架构。如果删除约束,那么您可能会破坏您的应用程序。这与您类似地更改所依赖的代码相同。

通常的做法是使用程序代码来治疗贫血对象。对于大多数情况,这不是最佳做法。

答案 3 :(得分:1)

为什么locationNum是一个int,实际上你引用另一个类的实例,比如说Location?如果您使用正确的对象引用,则可以在Schedule 中验证它们,并配置ORM(如果有)以强制引用完整性。

答案 4 :(得分:0)

回答你的问题:

1)我不认为你的班级会贫血,暂时不会是DTO,我认为没有问题。

2)如果你的验证是在业务层,那并不意味着你会得到一个无效的对象,因为验证仍然存在,通常你的验证过程会以某种方式抱怨InvalidDataException或者其他东西并赢了不允许你使用“损坏的”对象。如果null日期不是可接受的值(null通常无法进入数据库),那么business / dao层应该捕获此错误并阻止该应用程序使用该对象。