【问题标题】:Validating arguments to a method验证方法的参数
【发布时间】:2010-07-28 16:08:44
【问题描述】:

我有一个关于当参数包含在对象中时验证方法参数的最佳实践的问题。例如,如果您有:

public class Student {
   public int getStudentId();

   public String getStudentName();

   public String getStudentSSN();

   public double getStudentGpa();

   public String getStudentMajor();

   // Other student related getters
}

那么,我有一个方法:

public void printStudentReport(Student student);

在这个方法中,我需要执行涉及ID、姓名、GPA和专业的逻辑。所以,这些是必需的。不必填充所有其他学生吸气剂。是否可以先验证 Student 对象,然后验证我需要的这四种方法?我觉得这有点误导,因为我将此 Student 对象传递给此方法,但并非所有字段都是必需的,因此它实际上是一个半填充对象被发送到此方法。对我来说似乎很奇怪。

【问题讨论】:

标签: java validation methods argument-passing


【解决方案1】:

如果某些属性必须始终填充以使学生有效,则应考虑使用所需参数定义非默认构造函数,并从类中删除任何默认构造函数(如果需要,验证 getter 中的属性值)。这确保只能创建有效的学生对象。

如果其他属性对于学生来说真的是可选的,那么对我来说它看起来完全没问题。当然,您需要仔细考虑用例并仔细分析领域模型,以确定哪些参数是必需的,哪些是可选的。

【讨论】:

  • 这里的场景实际上是您回复中的第二段。报告方法中列出的那些不必设置为学生有效。
  • @smayers81 - 对象与结构相比的一大优势是能够保证对象始终处于有效状态。但是,我可以想象一个新学生还没有宣布专业并且没有 GPA,因为他们还没有上课。但是,好的默认值可能是“未声明”和 0.0,因此该对象仍然有效。 Student 的所有构造函数都应该需要 ID 和 Name,以及额外的构造函数来提供专业和/或 GPA。
【解决方案2】:

想想您正在创建的概念:学生报告。该方法仅使用一组学生数据并不重要,因为这些是您报告的当前要求。也许他们将来会改变。也许他们不会。但它似乎是正确的设计,因为它更能适应变化。

现在验证变得更加棘手。报告是否需要一种特殊的验证,不同于学生的正常验证?如果是这样,那么一定要在报告中验证它:

public void printStudentReport(Student student) {
  validateStudent(student);
  // print the report....
}

但如果验证对于一组客户端来说是通用的(可能是 printStudentReportsaveStudentInDatabase),那么您可以创建一个验证类:

public class FloogleStudentValidator { // or some good name that tells us what this validation does
  public void validate(Student student) { }
}

// ...

public void printStudentReport(Student student) {
  new FloogleStudentValidator().validate(student);
  // print the report....
}

对于不同类型的学生验证,您会有不同的课程。

但是,如果验证对整个系统来说是通用的,那么我更愿意将其放在 Student 类本身中,或者在填充到学生实例中时对其进行验证。

public void printStudentReport(Student student) {
  student.validate();
  // print the report....
}

【讨论】:

  • 在类似的想法中,您可以添加学生状态枚举或班级。一旦满足报告标准,它就会变为特定状态。
【解决方案3】:

如果您只需要 Student 的 4 个属性,那么我强烈建议您更改方法以单独获取它们。

这样您就可以将打印方法与 Student 分离,并带来所有好处。

【讨论】:

  • 它被称为printStudentReport 的事实暗示了它应该招收一名学生。即使它使用一组给定的值 today 并不意味着它不能更改为使用 moretomorrow
  • 我不太确定这是否总是有益。事实上,它会将调用者与StudentprintStudentReport() 的实际表示更紧密地联系在一起。如果你不能重用printStudentReport(),那么平衡这一点并没有太大的好处。如果从许多不同的地方调用该方法,更不用说代码膨胀和重复......
  • 是的,我需要四五个参数。单独通过它们似乎很奇怪。另外,正如 Jordao 所说,如果以后再添加 4 个呢?
  • 您必须权衡取舍。方法的参数改变的概率是多少?它似乎足以让您的提案没有吸引力。
【解决方案4】:

一种选择是让 Student 对象在创建时(以及在编辑期间)验证它的数据,因此当您传递 Student 对象时,您始终可以确保它们是有效的。

但这假设在程序的所有区域中,相同的验证约束将应用于 Student 对象,因此可能不是一个选项。

【讨论】:

    【解决方案5】:

    半填充对象很常见。特别是如果您无法控制填充对象的数据源。我说只验证 printStudentReport() 所需的学生字段就可以了。我经常编写类似的报告生成方法,这些方法根据必要的数据进行验证,但如果对象存在,则会提供来自对象的任何额外数据。

    【讨论】:

      【解决方案6】:

      您也可以尝试接口方法,而不是传递学生对象,而是传递接口。这意味着您可以拥有仅实现对象的该部分的学生对象。

      public void printStudentReport(MyInterface student)
      

      然后接口可以包含一个验证方法

      【讨论】:

        【解决方案7】:

        我想到的一个问题是逻辑是否真的是报告逻辑或学生逻辑。在报告中你可以编码:

          thing = (student.getX() + student.getY() ) * student.getZ();
        

        或者只是

          thing = student.getThing();
        

        我的看法是,有些东西可能属于学生。

        那么我们得到这样的情况,我们无法计算 事物,因为某些 X、Y 或 Z 未正确初始化。所以调用 getThing() 可能会抛出异常,但这感觉很奇怪。为什么一个对象应该提供一些 getThing() 功能,但后来却做不到?

        在我看来,您的 Student 课程需要重构。学生可以执行一组核心功能,它们足以生成某些报告。因此,您可以说 IRegistered 接口和更丰富的 IActiveStudent 接口。您的报表类需要 IRegistered,而其他类需要 IActiveStudent。

        各种学生对象在其生命周期中会改变它们的能力,就像毛毛虫变成飞蛾一样。您在毛毛虫上没有 fly() 方法,因此您的所有 lepidoptra 类都不需要 canYouFlyYet() 方法。

        【讨论】:

        • 完全同意。只是此时在游戏中重构Student类实在是行不通。另外,确实没有一个好的界面可以用于此。此报告只是临时打印的信息报告。
        猜你喜欢
        • 1970-01-01
        • 2022-10-26
        • 2011-06-03
        • 2013-09-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-03
        • 2019-04-22
        相关资源
        最近更新 更多