关于GUI与Logic类集成的方式的一个非常基本的问题

时间:2009-10-17 05:10:41

标签: c# design-patterns oop validation conventions

假设我有一个巨大的输入表单,当然代表类。我需要将此输入加载到类的实例中。这个输入显然包含(一些非常复杂的验证)检查,显然逻辑层已经包含那些输入验证。问题是我在用gui做什么。

我应该以非常丑陋的方式重写GUI中的所有验证吗?

或者我应该在逻辑层中编写一些静态方法,在gui和逻辑层中使用那些方法,但仍然创建自我验证的重复(首先gui验证自己,然后逻辑验证什么送到它)

或者我应该假设gui没问题,用try块包围使用逻辑层的相关代码,然后,如果抛出异常,通知用户SOMETHING不正确(没有给他机会)知道它是什么)

或者我应该公开异常,我以哪种方式向他公开参数,类和名称空间名称,这可能是他无法理解的。

或者我应该为每个错误制作一个特殊的异常类,这样就可以准确地告诉用户问题是什么,但是可能会产生数百个可能的异常

或者我应该将它与一般例外分开,每个人都包含enum描述错误的确切内容,然后捕获这些异常,并通过检查枚举通知用户究竟是什么问题,但通过不必要地捕获使应用程序更重一直例外。

或者我应该(有人向我提出这个,这不是我的想法,不要对我大喊:D)验证逻辑层中的输入,并仅在gui中检查它(似乎是我绝对可怕的解决方案:D)

更重要的问题 - 我应该在哪里学习这些东西? 通常我的直觉相当不错,但我不想不必要地发明轮子..(我很确定已经存在你每天都被轰炸的基本事物的惯例。)

非常感谢你!

3 个答案:

答案 0 :(得分:3)

当然,您应该验证用户输入。如果输入和验证逻辑与您所​​说的一样复杂,那么验证GUI中的输入更为重要,这样可以使用户明白期望的值是什么,并且,如果有任何错误, 它们是什么。如果您可以建议如何解决这些错误,可以获得奖励积分!

它确实无法帮助用户查看异常和异常详细信息 - 因此请尽量避免这种情况。

此外,由于您正在处理GUI中的输入验证,并且输入错误是一种期望,并且并非真正与众不同,因此使用Exceptions不一定是个好主意。用于检查某些内容是否有效的简单IsValid()方法更可取。我总是遵循规则,即“例外情况适用于特殊情况”。

因此,如果您接受GUI中的验证是一件好事,那么接下来的问题是:如何?

你说你已经有很多验证,但听起来你的验证逻辑不是单独提供的。我一直觉得有用的做法是将验证代码与其他业务逻辑分开。这允许您在适当的情况下重用验证逻辑,在这种情况下,允许您在业务对象和GUI之间共享相同的验证逻辑。显然,有许多设计方法和框架可以做到这一点,但基本原则是“关注点分离” - 保持验证逻辑分离,并使其可用。当你说“在逻辑层中编写一些静态方法,在gui和逻辑层中使用那些方法”时,你似乎在想同样的思路,这是一种被证明有用的方法。

为了清楚起见 - 我并不是建议您将验证逻辑放在GUI本身中。而是使GUI可以使用验证逻辑。应该在GUI中进行验证的唯一部分是从用户(提交到验证)获取输入的部分以及显示验证结果的部分(对用户)。

编辑:

我不想听起来像是一种特定的设计理念,但最近我一直在使用领域驱动设计原则做更多的工作。我发现它工作得很好,它解决了你提出的许多问题。关于SO的一些问题提供了更多关于它是什么以及某些资源在哪里的细节:

https://stackoverflow.com/questions/1353742/domain-driven-design-ddd-readings
What is domain driven design?

另外,请在此处阅读:http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/02/15/validation-in-a-ddd-world.aspx

编辑2:

也是一个有用的(和相关的)阅读:Business Objects, Validation And Exceptions

答案 1 :(得分:1)

我刚刚在一个项目中完成了类似的工作。我有3个相当大的表单和许多表示所需数据的类。每个类都有一个bool IsValid()方法。

当用户单击“保存”按钮时,将调用一个方法,该方法从表单上的输入项构建所有类。每个属性都有非常简单的验证(类型,默认值,如果没有设置,等等)。一旦构建了所有类(进入类似树的结构 - 一个包含许多其他类的顶级类),就会在父级上调用IsValid方法,而父级又在其所有子级上调用IsValid。

如果IsValid返回False,则父级的Errors属性将设置为IsValid调用失败的所有子级的Errors属性。然后,我在用户友好的视图中显示错误。

但是,有一些情况需要在点击保存按钮之前验证某些条件,我提供了相关类的方法。

我真的认为你根本不应该在GUI类中进行验证。每个班级应负责自己的验证要求。但是,我认为使用GUI向用户提供表单所需区域的“提示”是很好的,例如使用更改事件来启用或禁用表单的某些部分。

我认为,一般来说,以一种不需要GUI的方式编写所有逻辑和类是个好主意。如果您想创建一个控制台前端而不是Windows窗体,那该怎么办?您应该能够在不更改现有业务类的情况下将一个交换为另一个。

答案 2 :(得分:0)

这是一个很好的问题。将验证放在数据层中的想法是一个经典的OO概念,但是当橡胶遇到道路时它可能很尴尬。我喜欢将实体的验证规则放入自己的类中以便可以重用它们的想法;但是,还有更多需要考虑的问题。

我通常使用分层方法进行数据验证,顶层(表示层)包含最复杂和最有用的代码。在中间层和数据层中,验证的重点是在遇到无效数据时验证断言和抛出异常。我们的想法是,您希望表示层完全验证数据,但是如果无效数据通过,您希望保护业务逻辑并提供良好的诊断。您绝对不希望向用户显示原始异常,但最好保留异常信息以便您可以访问它。例如,您可以将异常跟踪写入日志文件和/或通过电子邮件将其发送给自己,和/或如果您认为用户可以处理它,则在适当的上下文中将其显示给用户。

当您考虑数据验证的细节时,创建一个“知道”如何验证自身的对象的经典想法变得没有它首次出现时那么有用。每一层验证都解决了一些不同的问题。即使基础业务规则驱动跨所有层的验证,对无效数据的响应也会根据代码的上下文而有所不同。主要区别在于,在表示层中,您确实希望专注于与用户清晰地沟通,并在面对无效数据时创建良好的用户体验。将这些代码内置到各个屏幕和控件中是有意义的。

绝对可取的是将简单的原子业务规则提炼为专用于验证的类中的简单函数或常量。您也可以按照建议将这些规则放在数据层类的静态函数中。主要的是只定义一次验证规则。例如,如果应用程序将值限制在10到100之间,则常量10和100应仅在代码中出现一次。但是这些常量或执行范围验证的简单函数将从不同层中的多个验证函数中使用。

在相关主题上,还可以定义仅包含验证常量和简单验证函数的类的验证程序集。然后可以将这些验证程序集加载到SQLCLR中并用于数据库层验证。通过这种方式,相同的验证定义可以跨越整个系统一直到数据库层。