好的代码风格可在任何地方引入数据检查?


10

我有一个足够大的项目,我再也无法保持每个方面的头脑。我正在处理其中的许多类和函数,并且正在传递数据。

随着时间的流逝,我注意到我一直在出错,因为我忘记了将数据传递给不同的函数时数据必须具有的精确格式(例如,一个函数接受并输出一个字符串数组,另一个函数是我后来写的,接受保留在字典等中的字符串,因此我必须将正在使用的字符串从数组中的字符串转换为字典中的字符串)。

为了避免总是要弄清楚什么地方断了,我开始将每个函数和类都视为“孤立实体”,因为它不能依赖外部代码为其提供正确的输入,而必须自己执行输入检查(或,在某些情况下,如果数据格式错误,请重铸数据。

这大大减少了我花费在确保传递的数据“适合”每个函数上的时间,因为当某些输入不正确(有时甚至纠正)时,类和函数本身会警告我,而我不会必须与调试器一起遍历整个代码,以找出问题的根源。

另一方面,这也增加了整体代码。
我的问题是,此代码样式是否适合解决此问题?
当然,最好的解决方案是完全重构项目,并确保数据具有所有功能的统一结构-但是由于该项目不断增长,因此我最终将花费更多的时间并担心干净的代码,而不是实际添加新内容。

(仅供参考:我仍然是一个初学者,因此,如果这个问题太天真,请原谅;我的项目是使用Python。)



3
@gnat相似,但是它没有回答我的问题,而是为OP提到的那个特定实例提供建议(“尽你所能”),这与我的一般查询有所不同。
user7088941 '18

2
“但是由于这个项目在不断增长,所以我最终将花费更多的钱,并且担心使用干净的代码,而不是实际添加新的东西。”-听起来很像您需要开始担心干净的代码。否则,由于现有代码的缘故,每增加一个新功能都会越来越难,您会发现生产力在不断下降。并非所有的重构都需要“完成”,如果由于涉及的现有代码而很难添加新的内容,则只需重构该具有
感性的

3
这是人们经常使用弱类型语言面临的问题。如果您不想或可以切换到更严格的类型的语言,答案就是“是的,此代码样式适合解决此问题”。下一个问题?
布朗

1
如果使用严格类型化的语言并定义了正确的数据类型,则编译器将为您完成此操作。
SD

Answers:


4

更好的解决方案是充分利用Python语言功能和工具。

例如,在函数1中,预期的输入是字符串数组,其中第一个字符串表示某物的标题,第二个字符串表示书目参考。在函数2中,预期的输入仍然是字符串数组,但是现在字符串的作用相反。

用可以缓解此问题namedtuple。它是轻量级的,并为数组的成员提供了简单的语义含义。

要获得某些无需切换语言的自动类型检查的好处,您可以利用类型提示。一个好的IDE可以使用它来通知您什么时候做一些愚蠢的事情。

您还似乎担心功能会在需求更改时过时。这可以通过自动测试来发现。

我并不是说手动检查永远都不适合,但是更好地使用可用的语言功能可以帮助您以更易于维护的方式解决此问题。


+1可以将我指向namedtuple其他所有好东西。我并不是没有namedtuple-虽然我了解自动化测试,但我从未真正使用过它,也没有意识到在这种情况下它会对我有多大帮助。所有这些似乎都和静态分析一样好。(自动测试甚至可能更好,因为我可以捕获静态分析中无法捕获的所有细微内容!)如果您知道其他任何内容,请告诉我。我将让问题开放一段时间,但是如果没有其他答案,我会接受您的。
user7088941 '18

9

好的,实际问题在此答案下方的注释中描述:

例如,在函数1中,预期的输入是字符串数组,其中第一个字符串表示某物的标题,第二个字符串表示书目参考。在函数2中,预期的输入仍然是字符串数组,但是现在字符串的作用相反

这里的问题是使用顺序表示语义的字符串列表。这是一种容易出错的方法。相反,您应该使用两个名为title和的字段创建一个自定义类bibliographical_reference。这样,您就不会将它们混淆,并且将来可以避免此问题。当然,如果您已经在很多地方使用过字符串列表,则需要进行一些重构,但是请相信我,从长远来看,它会更便宜。

动态类型语言中的常见方法是“鸭子类型”,这意味着您实际上并不关心传递的对象的“类型”,只关心它是否支持调用它的方法。就您而言,您将仅bibliographical_reference在需要时阅读称为的字段。如果在传递的对象上不存在此字段,则将出现错误,这表明将错误的类型传递给函数。这和其他类型检查一样好。


有时问题甚至更加棘手:我传递了正确的类型,但是输入的“内部结构”使函数混乱:例如,在函数1中,预期的输入是字符串数组,其中第一个字符串表示某物的标题,第二个为参考书目。在函数2中,预期的输入仍然是字符串数组,但是现在字符串的作用是相反的:第一个字符串应该是书目参考,第二个应该是书目参考。我猜对于这种检查是否合适?
user7088941

1
@ user7088941:通过使用带有两个字段的类可以轻松解决您描述的问题:“标题”和“ bibliographical_reference”。您不会混淆。依赖于字符串列表中的顺序似乎很容易出错。也许这是潜在的问题?
JacquesB '18

3
这就是答案。Python是一种面向对象的语言,而不是面向集成程序的字符串列表(或任何其他语言)。因此,使用对象。对象负责管理自己的状态并强制执行自己的不变式,而其他对象永远不会破坏它们(如果设计正确)。如果非结构化或半结构化数据从外部进入系统,则需要在系统边界进行一次验证和解析,并尽快将其转换为丰富的对象。
约尔格W¯¯米塔格

3
“我真的会避免不断的重构”-这是您的问题所在。好的代码仅来自重构。大量的重构。由单元测试支持。尤其是当组件需要扩展或发展时。
布朗

2
我知道了 +1获得所有不错的见解和评论。并感谢所有人的宝贵帮助!(虽然我正在使用一些类/对象,但我将它们散布在提到的列表中,如我现在所见,这并不是一个好主意。问题仍然是如何最好地实现这一点,在这里我使用了JETM回答的具体建议,这在实现
无错误

3

首先,您现在正在体验的是代码异味 -尝试记住导致您意识到异味的原因并尝试磨练“精神”鼻子,因为您越早注意到代码异味越早-越容易-您就能解决潜在的问题。

为了避免总是要弄清楚什么地方断了,我开始将每个函数和类都视为“孤立的实体”,因为它不能依赖外部代码为其提供正确的输入,而必须自己执行输入检查。

防御编程-这种技术被称为-是有效且经常使用的工具。但是,与所有事物一样,使用正确的数量很重要,检查量太小,您就不会发现问题,太多的话,您的代码就会变得过分膨胀。

(或者,如果数据格式错误,则在某些情况下,请重铸数据)。

那可能不是一个好主意。如果您注意到程序的一部分正在使用格式错误的数据调用函数,请FIX THAT PART,不要更改被调用的函数以无论如何都能消化不良数据。

这大大减少了我花费在确保传递的数据“适合”每个函数上的时间,因为当某些输入不正确(有时甚至纠正)时,类和函数本身会警告我,而我不会必须与调试器一起遍历整个代码,以找出问题的根源。

从长远来看,提高代码的质量和可维护性可以节省时间(从某种意义上讲,我必须再次警告您内置于某些函数中的自校正功能-它们可能是错误的隐患。程序不会崩溃和刻录并不意味着它正常工作...)

最后回答您的问题:是的,防御性编程(即,验证提供的参数的有效性)在健康方面是一个不错的策略。就是说,就像您自己说的那样,您的代码是不一致的,我强烈建议您花一些时间来重构有异味的部分-您说您不想一直担心干净的代码,而花更多的时间在“清洗”不是新功能......如果你不保持你的代码干净,你可能会花两倍的时间“保存”从没有跟上压扁上一个错误干净的代码,将有一个很难实现的新功能- 技术债务 CAN暗恋你


1

没关系。我曾经在FoxPro中进行编码,在这里我几乎在每个大型函数中都有一个TRY..CATCH块。现在,我使用JavaScript / LiveScript进行编码,很少检查“内部”或“私有”功能中的参数。

“要检查的数量”对所选项目/语言的依赖性要大于对代码技巧的依赖性。


1
我猜是TRY ... CATCH ... IGNORE。您做了与OP要求相反的事情。恕我直言,他们的目的是避免不一致,而您要确保程序在被击中时不会崩溃。
maaartinus

1
@maaartinus是正确的。编程语言通常会为我们提供易于使用的结构,以防止应用程序爆炸—但是,编程语言给我们提供了结构,以防止不一致似乎更难使用:据我所知,不断重构所有内容并使用最佳容器化的类应用程序中的信息流。这正是我要问的-是否有更简单的方法来解决此问题。
user7088941

@ user7088941这就是为什么我避免使用弱类型语言。Python真是太棒了,但是对于任何更大的东西,我都无法跟踪在其他地方所做的事情。因此,我更喜欢Java,它比较冗长(Lombok和Java 8功能不多),具有严格的类型输入和用于静态分析的工具。我建议您尝试一些严格类型的语言,因为我不知道如何解决它。
maaartinus

这与严格/松散类型参数无关。关于知道该参数正确。即使您使用(整数4字节),也可能需要检查它是否在例如0..10的范围内。如果您知道该参数始终为0..10,则无需检查它。FoxPro不具有例如关联数组,它很难与它的变量,其范围等操作..这就是为什么你必须检查检查检查..
迈克尔四

1
@ user7088941不是OO,但是有一个“快速失败”规则。每个非私有方法都必须检查其参数,并在出现任何错误时抛出该异常。无需尝试捕获,也无需尝试修复它,只需将其高高吹起即可。当然,在更高的层次上,异常会得到记录和处理。由于您的测试可以事先发现最多的问题,并且没有任何问题被隐藏,因此代码的收敛速度比容错的要快得多。
maaartinus
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.