关于软件设计:哪里必须检查参数?

时间:2011-02-27 15:29:45

标签: validation software-design

想象一下,我有一个应用程序向用户请求名称,类别列表。当用户单击“保存”按钮时,应用程序会将名称和类别保存到数据库中。

我有一个从UI获取名称和类别的图层。此图层检查是否有名称(长度> 0的字符串)。如果这是正确的,它会将一个类别的名称传递给另一个图层。注意:category是radiobutton list,其中始终选择一个项目。

在第二层,应用程序将根据类别选择合适的类来保存名称。

在最后一层,类会将此名称保存在数据库中。在这堂课上,我将检查姓名是否为空。

我的问题是:检查方法输入参数的正确位置在哪里?在每一层?也许,我将在其他开发项目上使用这些层。

我的例子是否正确?也许,我可以在数据库层上保留验证并向UI层引发异常。

4 个答案:

答案 0 :(得分:3)

  

检查方法输入参数的正确位置在哪里?在每一层?

是的,在每一层上。这称为defense in depth

每层都有不同的理由:

  • UI /客户端代码:保持响应并避免数据无效时的往返
  • 业务层:确保保留业务规则
  • 数据层:确保通过
  • 传递有效数据

答案 1 :(得分:3)

一般而言,就验证最终持久的输入的较大问题而言,最好是:

  • 将输入参数转换为a 完全封装的业务对象为 收到后尽快 它。

  • 早期验证然后快速失败 等到你到达更低层 - 浪费资源,浪费时间,可能更复杂(更多的事情要做 回滚)。

  • 验证业务逻辑一次并成为其中的一部分 你的对象实例化过程。 (但请注意,视图逻辑和持久性逻辑的验证可能需要在其他层完成,并且与业务逻辑分开)

  • 模拟对象的持久化方式 使用ORM(例如,Hibernate) 你可以纯粹地工作 内存中的对象级别和离开 持久性作为一种实现 详情。把业务逻辑集中在 对象层。

就方法验证本身而言,我同意Oded - 在每一层,并且应该在方法输入时立即完成。同样,这是快速失败方法的一部分。但是,正如我上面提到的,这并不意味着您在每个方法(或每个层)验证业务逻辑。我只是指验证输入的基本实践(通过断言或显式检查和抛出的异常)。

答案 2 :(得分:2)

我始终不同意有关每个图层的建议。这听起来太教条了。

所有设计都代表了功能和成本之间的选择。您的验证选择也应该反映出来。

我认为您的决定应考虑到图层的共享和重用。

如果数据库由多个应用程序共享,则数据库不能依赖于正确验证的每个应用程序。在这种情况下,数据库必须验证以保护自己。

在SQL注入攻击的这个时代,我认为在到达持久层之前绑定和验证是必须的。模式应该做必要的事情以确保参考和业务完整性(例如对列的唯一约束和所需的“非空”),但是在访问数据库之前应该进行其他验证。

如果数据库完全归一个且只有一个应用程序所有,并且有一个服务是唯一进入数据的网关,则该服务可以进行验证。可以免除在数据库层上复制验证的成本。

同样在UI和服务层之间。

客户端和服务层的双重验证很常见,因为服务由面向服务的体系结构中的许多客户端共享。今天,您的服务可能会被基于浏览器的UI使用;突然间,还有一大堆移动应用程序,它们也吵着要服务。在这种情况下,服务绝对必须验证并绑定每个请求。

没有制造商对每个表面进行抛光以达到镜面质量。有时粗糙的铸造表面是允许的,因为磨削和抛光的好处可以忽略不计,而且成本太高。

与软件相同。

我不喜欢教条式的陈述。最好了解您选择的含义。了解规则以及何时打破它们。

答案 3 :(得分:0)

如果您要进行非常松散的耦合,其他应用程序也会使用这些相同的层,那么我建议您在每一步都进行输入检查。由于每个类/方法都不应该知道在堆栈中运行的其他类/方法的期望,每个类/方法应该单独强制执行其要求。

  • 如果用户界面要求在单击按钮时文本框中包含值,则应相应地进行验证。
  • 如果业务逻辑要求名称永远不为null / empty,则不应允许将null / empty值放在该属性中。
  • 如果数据层对要求存在值的字段有约束,则应在尝试保留数据之前检查是否存在值。