【问题标题】:A very basic question about the way GUI integrated with the Logic classes关于 GUI 与逻辑类集成方式的一个非常基本的问题
【发布时间】:2010-12-07 13:51:35
【问题描述】:

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

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

或者我应该在逻辑层中编写一些静态方法,在 gui 和逻辑层中使用这些方法,但仍然创建验证自身的副本(首先 gui 验证自身,然后逻辑验证什么发送给它)

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

或者我应该公开异常,以何种方式向他公开参数、类和命名空间名称,他可能不会理解。

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

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

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

还有更重要的问题 - 我应该在哪里学习这些东西? 通常我的直觉很好,但我不想不必要地发明轮子..(我很确定你每天都在做这些基本的事情)。

非常感谢!

【问题讨论】:

    标签: c# design-patterns oop validation conventions


    【解决方案1】:

    当然,您应该验证用户输入。如果输入和验证逻辑像你说的那样复杂,那么在 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

    【讨论】:

    • 这也是我的首选选项,但唯一的缺点是这些验证函数实际上会不必要地用于相同的输入两次(来自 gui,然后在逻辑层中使用八次)。我应该编写指示是否验证 gui 的 bool 参数吗?丑陋与性能...
    • 一般来说,我工作过的应用程序是服务器/客户端。在那些情况下,实际上在许多分层设计中,服务层和表示 (GUI) 层都负责执行验证(例如,如果恶意用户绕过验证怎么办)。如果你没有这个问题(例如,假设它只是一个 GUI 应用程序,没有物理分离的层),我更喜欢在 GUI 中进行验证,它对用户有用。但是,如果有任何方法可以绕过 GUI 验证,那么这两个地方都需要它。
    • 至于性能 - 在您证明存在性能问题之前,不要担心。如果执行两次验证实际上不会导致问题,请执行两次。如果它可以带来更好的设计/架构,则尤其如此。
    【解决方案2】:

    我刚刚在一个工作项目中完成了一件非常相似的事情。我有 3 个相当大的表格和一大堆代表所需数据的类。每个类都有一个 bool IsValid() 方法。

    当用户单击保存按钮时,会调用一个方法,该方法会根据表单上的输入项构建所有类。每个属性都有非常简单的验证(类型、未设置的默认值等)。一旦所有的类都建立起来(形成一个树状结构 - 一个包含大量其他类的顶级类),就会在父级上调用 IsValid 方法,然后在其所有子级上调用 IsValid。

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

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

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

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

    【讨论】:

    • 我知道,这也是我的想法。但最重要的问题是.. 我在哪里学习这些约定?通常我的直觉很好,但我不想不必要地发明轮子。
    【解决方案3】:

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

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

    当您考虑数据验证的细节时,创建一个“知道”如何验证自身的对象的经典想法变得不像最初出现的那么有用。每一层验证都解决了一些不同的问题。即使底层业务规则驱动所有层的验证,对无效数据的响应也会因代码的上下文而异。主要区别在于,在表示层,你真的希望专注于与用户进行清晰的沟通,并在面对无效数据时创造良好的用户体验。这段代码应该内置到单独的屏幕和控件中是有道理的。

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

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

    【讨论】:

      猜你喜欢
      • 2014-09-25
      • 2021-06-03
      • 2019-10-18
      • 1970-01-01
      • 1970-01-01
      • 2021-12-06
      • 2014-08-15
      • 1970-01-01
      • 2019-10-30
      相关资源
      最近更新 更多