代码之家  ›  专栏  ›  技术社区  ›  jason

业务验证逻辑代码气味

  •  10
  • jason  · 技术社区  · 17 年前

    考虑以下代码:

    partial class OurBusinessObject {
        partial void OnOurPropertyChanged() {
            if(ValidateOurProperty(this.OurProperty) == false) {
                this.OurProperty = OurBusinessObject.Default.OurProperty;
            }
        }
    }
    

    也就是说,当 OurProperty 在……里面 OurBusinessObject 如果该值无效,请将其设置为默认值。这种模式让我觉得是代码的味道,但这里(我的雇主)的其他人不同意。你有什么想法?

    :我被要求补充一个解释,为什么这被认为是可以的。其想法是,业务对象可以验证自己的属性,并在验证失败的情况下设置干净的默认值,而不是让业务对象的生产者验证数据。此外,人们认为,如果验证规则发生变化,业务对象生产者就不必改变他们的逻辑,因为业务对象将负责验证和清理数据。

    14 回复  |  直到 12 年前
        1
  •  17
  •   Grzenio    17 年前

    这太可怕了。祝你在生产环境中调试问题时好运。它唯一能导致的是覆盖漏洞,这些漏洞只会在其他地方弹出,在那里根本不清楚它们来自哪里。

        2
  •  3
  •   chills42    17 年前

    我想我必须同意你的观点。这肯定会导致逻辑意外返回默认值的问题,这可能很难调试。

    至少,应该记录此行为,但这似乎更像是抛出异常的情况。

        3
  •  3
  •   Joel Coehoorn    17 年前

    在我看来,这只是症状,而不是实际问题。真正发生的事情是 OurProperty 未能保留原始值以供使用 OnOurPropertyChanged 活动。如果你这样做,突然之间,就更容易对如何继续做出更好的选择。

    就此而言,你真正想要的是 OnOurPropertyChanging 由设置者引发的事件 之前 任务确实发生了。通过这种方式,您可以在一开始就允许或拒绝分配。否则,在一小部分时间里,你的对象是无效的,这意味着该类型不是线程安全的,如果你认为并发性是一个问题,你就不能指望一致性。

        4
  •  2
  •   AwesomeTown    17 年前

    这绝对是一种有问题的做法。

    如何将无效值分配给此属性?这难道不意味着调用代码中的某个地方存在错误吗?在这种情况下,你可能想马上知道?或者用户输入了错误的内容,在这种情况下 他们 应该马上通知吗?

    一般来说,“快速失败”使追踪bug变得容易得多。在幕后默默地指定默认值类似于“魔法”,只会给必须维护代码库的人带来困惑。

        5
  •  1
  •   Andy Mikula Eric Mickelsen    17 年前

    暂且不谈“代码气味”这个词,你可能是对的——根据它来自哪里,默默地改变值可能不是一件好事。最好确保你的值是有效的,而不是仅仅恢复到默认值。

        6
  •  1
  •   Reed Copsey    17 年前

    我强烈建议在设置属性之前对其进行重构以进行验证。

    你总是可以有一个更像这样的方法:

    T GetValidValueForProperty<T>(T suggestedValue, T currentValue);
    

    或者甚至:

    T GetValidValueForProperty<T>(string propertyName, T suggestedValue, T currentValue);
    

    如果这样做,在设置属性之前,您可以将其传递给业务逻辑进行验证,业务逻辑可以返回默认属性值(您的当前行为)或(在大多数情况下更合理)返回当前值,因此设置没有效果。

    这更像是:

    T OurProperty
    {
        get
        {
            return this.propertyBackingField;
        }
        set
        {
            this.propertyBackingField = this.GetValidValueForProperty(value, this.propertyBackingField);
        }
    }
    

        7
  •  0
  •   Brad Barker    17 年前

    它可能有也可能没有“气味”,但我更倾向于“是的,它有气味”。

    将OurProperty设置为默认值是否有逻辑上的原因,或者在代码中这样做只是方便吗?尽管在实践中不太可能,但有可能设计出一种预期行为的场景,但我猜在大多数情况下,你应该抛出一个异常并在某个地方干净地处理它。

    将该值设置为默认值会让您更接近还是远离

        8
  •  0
  •   Nuno Furtado    17 年前

    更改完成后,您是否正在验证更改?应在更改busyness属性之前进行验证。

        9
  •  0
  •   JeremyWeir    17 年前

    在不了解上下文或业务规则的情况下很难说。不过,一般来说,你应该在输入时进行验证,也许在持久化之前再进行一次验证,但你这样做的方式并不能真正让你进行验证,因为你不允许属性包含无效值。

        10
  •  0
  •   SingleNegationElimination    17 年前

    我认为,如果要求使用无效值,您的验证逻辑应该引发异常。如果你的消费者想要使用默认值,它应该明确地要求使用,要么通过一个特殊的、有记录的值,要么通过另一种方法。

    我认为唯一可以原谅的例外是,对大小写进行规范化,比如在电子邮件字段中检测重复项。

        11
  •  0
  •   George Mauer    17 年前

    此外,为什么世界上这是片面的?除了生成的代码框架之外,对任何东西使用分部类本身都是一种代码味道,因为你可能会用它们来隐藏复杂性,无论如何都应该把复杂性分开!

        12
  •  0
  •   Stephen P. in Roswell    17 年前

    我同意Grzenio的观点,并补充说,在域层(即业务对象)处理验证错误的最佳方法是生成异常。该异常可能会一直传播到UI层,在那里可以处理并与用户交互纠正。然而,根据所涉及的功能和技术,这可能并不总是可行的,在这种情况下,您可能应该在UI层进行验证(可能是在域层之外)。这不太理想,但可能是你唯一可行的选择。无论如何,将其设置为默认值是一件可怕的事情,会导致几乎不可能诊断的微妙错误。如果大规模地进行,你很快就会有一个无法维护的系统(特别是如果你没有单元测试支持你)。

        13
  •  0
  •   jason    17 年前

        14
  •  0
  •   Mike Christiansen    17 年前

    我会说,实现PropertyChanging并允许业务逻辑批准/拒绝一个值,然后对无效值抛出异常。

    这样,您就永远不会有无效的值。而且你永远不应该改变用户的信息。如果用户在数据库中添加一个条目,并为自己的记录跟踪它,该怎么办?你的代码会将值重新分配给默认值,而他现在正在跟踪错误的信息。最好尽快通知用户。