业务验证逻辑代码异味

10

考虑以下代码:

partial class OurBusinessObject {
    partial void OnOurPropertyChanged() {
        if(ValidateOurProperty(this.OurProperty) == false) {
            this.OurProperty = OurBusinessObject.Default.OurProperty;
        }
    }
}
也就是说,当 OurBusinessObject 中的 OurProperty 值改变时,如果该值无效,则将其设置为默认值。这种模式让我觉得是代码异味,但我的雇主中其他人不同意。你有什么想法吗?
编辑后添加:我被要求解释为什么认为这样做是可以的。这个想法是,业务对象的生产者不需要验证数据,业务对象可以验证自己的属性,并在验证失败时设置干净的默认值。此外,如果验证规则更改,业务对象生产者无需更改其逻辑,因为业务对象将负责验证和清理数据。
14个回答

17

这太糟糕了。在生产环境中尝试调试问题会很困难。唯一可能带来的结果是掩盖漏洞,这些漏洞只会在其他地方突然出现,而它们的来源显然不明。


我大部分同意,但如果预计会出现错误数据,一些属性可以安全地默认。我们真的不知道这里的情况是什么。 - Ed S.
如果属性可以安全地使用默认值,那么为什么一开始还要提示用户输入呢?你无论如何都忽略了用户提供的内容! - rp.

3

我认为我必须同意你的看法。这肯定会导致逻辑意外返回到默认值,这可能非常难以调试。

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


3
对我来说,这似乎是症状,而不是实际问题。真正的问题在于 OurProperty 的设置器未能保留原始值以供 OnOurPropertyChanged 事件使用。如果你这样做,突然之间就更容易做出更好的选择了。
事实上,你真正想要的是一个 OnOurPropertyChanging 事件,它从设置器中触发,在分配实际发生之前触发。这样你就可以允许或拒绝首次分配。否则,你的对象在一小段时间内是无效的,这意味着该类型不是线程安全的,如果你考虑并发是个问题,那么你不能指望保持一致性。

2

这绝对是一个值得质疑的做法。

如何才能将无效的值分配给此属性?这不会说明调用代码中存在错误吗?在这种情况下,您可能希望立即得知。或者用户输入了错误的内容,这时应该立即通知他们

总的来说,“快速失败”可以更轻松地跟踪错误。在幕后默默地分配默认值类似于“魔术”,只会让维护代码库的人感到困惑。


1

我强烈建议重构它,在设置属性之前进行验证。

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

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

或者甚至:

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

如果您这样做,在设置属性之前,您可以将其传递给业务逻辑进行验证,业务逻辑可以返回默认属性值(当前行为)或(在大多数情况下更合理),返回currentValue,因此设置没有影响。
这将更多地用于:
T OurProperty { get { return this.propertyBackingField; } set { this.propertyBackingField = this.GetValidValueForProperty(value, this.propertyBackingField); } }
您做什么并不重要,但在更改当前值之前进行验证非常重要。如果在确定新值是否正确之前更改值,则从长远来看会有麻烦。

1

除了对“代码异味”这个术语的厌恶之外,你可能是正确的——取决于它来自哪里,默默地更改值可能不是一件好事。最好确保您的值有效,而不仅仅是恢复默认值。


0

它可能会或可能不会“有味道”,但我更倾向于“是的,它有味道”。

将OurProperty设置为默认值是否有逻辑上的原因,还是在代码中这样做只是方便?虽然在实践中可能不太可能构造出预期行为的情况,但我猜想在大多数情况下,您应该抛出异常并在某个地方进行干净的处理。

将值设置为默认值是否使您更接近或远离应用程序应该工作的功能规范描述?


0

你是在变更完成后进行验证吗?应该在修改业务属性之前进行验证。

回答你的问题:代码片段中提出的解决方案可能会在生产环境中产生大问题,因为你不知道默认值是否由于无效输入而出现,还是由于其他原因将值设置为默认值。


0

不知道上下文或业务规则的情况下很难说。一般来说,你应该在输入时进行验证,并且可能还要在持久化之前再验证一次,但是你目前的方式并不能真正让你进行验证,因为你不允许属性包含无效值。


0

我认为你的验证逻辑应该在使用无效值时引发异常。如果您的消费者想要使用默认值,它应该显式地请求,可以通过特殊的、经过文档记录的值或其他方法进行请求。

我能想到的唯一可原谅的异常类型是像在电子邮件字段中规范化大小写以检测重复项等情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接