数据验证有什么问题?李斯科夫替代原则与它有什么关系?



如果您有时问自己一个问题:“用这种方法对我来说一切都还好吗?”,然后在“该怎么办”和“最好以防万一”之间进行选择,然后欢迎您...

校正:如lorc0xd34df00d所述 ,下面讨论的内容称为相关类型。 您可以在这里阅读有关它们的信息 。 好吧,下面是带有我对此想法的源文本。

在开发过程中,经常需要验证某些算法的数据有效性。 正式地,这可以描述如下:假设我们获得了某种数据结构,检查其值是否符合一定范围的允许值(ODZ),然后将其传递。 随后,可以对相同的数据结构进行相同的验证。 如果结构保持不变,则重新检查其有效性显然是不必要的操作。

尽管验证确实很长,但问题不仅仅在于性能。 更令人不快的是额外的责任。 开发人员不确定是否需要再次检查结构的有效性。 相反,除了不必要的验证之外,我们还可以假定没有任何验证,而错误地假定该结构已经过验证。

因此,在期望结构经过验证的方法中,如果其值超出可接受值的某个范围,则可能会导致故障。

其中存在一个显而易见的更深层次的问题。 实际上,有效的数据结构是原始结构的子类型。 从这个角度来看,仅接受有效对象的方法的问题等同于以下虚构语言的代码:

class Parent { ... } class Child : Parent { ... } ... void processValidObject(Parent parent) { if (parent is Child) { // process } else { // error } } 

同意,现在的问题更加清楚了。 摆在我们面前的是对Liskov替代原则的典型违反。 例如, 在此处阅读违反替代原则的原因何在。

可以通过为原始数据结构创建子类型来解决传输无效对象的问题。 例如,您可以通过工厂创建对象,该工厂根据原始结构返回有效的子类型对象或null。 如果我们更改期望有效结构的方法的签名,以便它们仅接受子类型,则问题将消失。 除了确信系统可以正常工作之外,每平方厘米代码的验证次数也会减少。 另一个优点是,通过此类操作,我们将验证数据的责任从开发人员转移到了编译器。

在Swift中,在语法级别上,解决了检查null的问题。 这个想法是将类型分为可空和不可空类型。 同时,它以糖的形式制成,从而程序员无需声明新类型。 声明变量类型时,ClassName保证变量为非零,并且声明ClassName时? 变量为空。 同时,类型之间存在协方差,也就是说,您可以将ClassName类型的对象传递给接受ClassName的方法。

这个想法可以扩展到用户定义的DLD。 为对象提供包含以该类型存储的DLD的元数据将消除上述问题。 以某种语言获得对这种工具的支持会很好,但是也可以使用继承和工厂在“常规” OO语言(例如Java或C#)中实现此行为。

数据验证的情况再次证明了OOP中的实体不是来自现实世界,而是来自工程需求。

UPD:正如评论中正确指出的,只有当我们获得更高的可靠性并减少相同验证的次数时,才值得创建子类型。

此外,本文缺少示例。 让一些文件路径在入口处出现。 在某些情况下,我们的系统适用于所有文件,在某些情况下仅适用于我们有权访问的文件。 接下来,我们要将它们转移到不同的子系统,这些子系统也可以使用可访问文件和不可访问文件。 此外,这些子系统甚至进一步传输文件,在此尚不清楚文件是否可用。 因此,在任何可疑的地方,都会出现访问检查,或者相反可能会忘记访问检查。 因此,由于广泛的歧义和检查,系统将变得更加复杂。 但是这些检查会加载磁盘,并且通常很重。 您可以将此检查缓存在布尔字段中,但这不会使我们免于需要验证的事实。 我建议将检查的责任从开发人员转移到编译器。

Source: https://habr.com/ru/post/zh-CN476220/


All Articles