找出代码中的漏洞——第三方代码、测试和类
使用第三方代码处理
组织你的测试
安排你的课程。
当我开始学习代码整洁系列课程时,我的目的不仅是为了巩固自己对这方面的知识,更是为了将我所获得的知识传递出去。
这套丛书还可以作为本书的快速指南。当我需要回忆某些主题时,可以更快地找到所需内容。
在第一篇文章中,我谈到了名称、函数和注释。
在第二篇文章中,我谈到了格式、对象和数据结构,以及如何处理错误。
在上一篇文章中,我讨论了如何处理第三方代码,以及如何组织测试和类。
除了这 9 个主题之外,《代码整洁之道》一书还讨论了系统处理、Java 的一些其他主题,并且还涵盖了如何识别一些代码异味。
我决定不涵盖这些主题,因为我想让这个系列更通用,更适合所有编程语言。另外,我也没有太多从零开始构建系统的实践经验,而且我认为专门讲解重构的系列文章能更好地解决代码异味问题,因为《代码整洁之道》这本书在这方面讲得比较浅显。
话虽如此,我们还是先来看看今天文章的主题吧:
使用第三方代码处理
我们很少完全独立完成代码。我们通常会使用一些库来辅助开发,而且大多数情况下,都会有同事对代码做出贡献。
学习如何处理代码边界非常重要,要明确我们的代码在哪里结束,对方的代码在哪里开始,这样我们才能更轻松地维护系统的正常运行。
使用第三方代码的一个问题是,它们通常具有比我们需要的更多的功能,这可能会导致我们的代码运行异常。
封装你的图书馆!
解决此问题的最佳方法是将我们将要使用的库封装在一个类中,并且只提取我们需要的方法。
将第三方库封装到自己的类中的另一个优点是,这样做可以更容易地更改库版本,甚至更换库本身。
假设我们的代码中有一个连接到支付服务的库。当我们想要更换该服务时,只需修改我们创建的类即可。这样,我们就可以调整方法,而无需遍历整个代码,将所有对旧库的引用更改为新库的引用。
探索第三方代码
当我们想在代码中添加新的库时,需要了解它的功能和工作原理。在生产环境中使用我们不了解的代码可能会导致一些问题。
要了解我们想要添加的库的行为,最好的方法是编写测试用例,针对我们希望库解决的各种情况进行测试,并确保所有测试都能通过。这样,我们不仅可以弄清楚代码的工作原理,而且这些测试用例对于任何版本或库的更改都非常有用。
如果更改版本并运行测试,您将会发现哪些行为不再有效并进行修正。
如果让我用几个主题来概括你代码中第三方库的使用情况,它们可以概括为:
- 务必将第三方代码与您自己的代码明确区分开来。
- 编写好的测试用例,检验第三方库的行为是否符合你的代码预期。需要注意的是,你是在测试你的代码,而不是库本身。
- 确保我们的代码不与任何第三方代码交互。依赖自己能够控制的东西总比依赖自己无法控制的东西要好。
- 封装并创建适配器,以便我们的代码尽可能少地引用它,并且我们可以在出现任何问题时更换库。
组织你的测试
编写测试时,我们最应该关注的是测试的可读性。
在测试代码中,可读性甚至比生产代码更重要。
编写测试时,我们应该避免编写与测试无关的细节,以便将来需要(重新)修复此测试的人无需理解不必要的代码就能知道发生了什么。
每个测试使用一个断言
理想情况下,每个测试只有一个断言,这样当发生这种情况时,就能清楚地知道测试的哪个部分出错了。
这条规则可能会导致多次重复测试。为了解决这个问题,你可以使用before重复执行的代码块。
如果重复太多,我们甚至可以在每个测试中使用多个断言,但要非常谨慎地保持断言的数量较少。
或者,以我(不是鲍勃叔叔)的意见来说,你可能无法解决这个问题,因为过多的DRY 原则会降低代码的可读性,而可读性在测试时应该始终是你的首要考虑因素。
或许更好的规则是,每次测试应该只测试一个概念。
为了保持简洁,并且方便跟踪测试过程,一次只能进行一项测试,这一点非常重要。
鲍勃叔叔用首字母缩写词来帮助记住干净考试的规则。
第一的
- 快速:测试应该很快。如果耗时太长,我们就不想运行这些测试。
- 独立性:测试不应依赖于其他测试才能运行。应该可以随机运行所有测试而不会导致程序崩溃。
- 可重复性:测试应该可以在任何环境下运行,无论是质量保证环境、生产环境还是你家里的电脑。
- 自验证:测试结果应始终为布尔值,以表明测试是否通过。
- 及时性:测试用例应该在编写生产代码之前编写,这是最佳时机。如果拖延,你可能会发现编写测试用例过于困难,最终放弃测试(测试驱动开发)。
安排你的课程。
在Java中,类的组织顺序应该是这样的:
- 变量列表。
- 公共静态常量。
- 私有静态变量。
- 实例化私有变量。
- 公共职能。
- 由公共函数调用的私有函数应该紧跟在调用它的公共函数下方。
然而,在 Ruby 和其他一些语言中,所有私有方法都必须集中编写,并且要写在所有公共代码之后。理想情况下,它们的顺序应该与公共方法调用它们的顺序一致。
班级规模应该较小。
当我们说一个函数必须很小时,我们指的是它必须只有几行代码。
我们说班级规模要小,意思是说班级必须遵循单一责任原则。
但我们如何判断一个班级是否承担了过多的责任呢?
判断一个班级承担了太多责任的第一个迹象就是它的名称。
类的命名应该具有解释性,以便我们能够理解它们的功能。如果需要用到“and”、“or”、“if”和“but”这样的语句,说明它们已经承担了不止一项功能。
在下面的示例中,我们可以看到该类SuperDashboard查找最后一个获得焦点的组件并更改构建版本,因此它有两个职责。
public class SuperDashboard {
public Component getLastFocusedComponent();
public int getMajorVersionNumber();
public int getMinorVersionNumber();
public int getBuildNumber();
}
解决此问题的一种方法是为每个职责创建一个类。
有些人抱怨说,课程太多,很难找到自己想学的课。
但仔细想想,如果课程内容很多,我们也会很难找到想要的东西。课程越多,就好像有很多抽屉,每个抽屉都贴着标签一样。而课程越少,抽屉就越多,但每抽屉都塞得满满的。
关于凝聚力的一点说明
在理想情况下,类中的函数应该只与该类中创建的变量进行交互,但由于这是不可能的,我们需要我们的类具有内聚性。
重构后新增的类。
我们在重构代码时可以观察到一种流程,这种流程可能会导致创建更多类:
- 当我们把一个大函数拆分成几个小函数时,我们会觉得有必要共享变量。
- 我们想创建类实例变量,这样函数就不需要参数了,但是为什么要创建只有少数函数会用到的实例变量呢?这看起来没什么意义。
- 这难道不是暗示着可以创建新的课程吗? :D
组织一次变革的课堂。
我们需要记住,一个类必须遵循“开放-封闭”原则,即对扩展开放,对改变封闭。
有时,我们的类会封装一个不断变化的第三方库。遇到这种情况,我们可以使用一个父类来连接到该库,并随着库的更新创建多个子类来实现新的库实现。
图片来自Sarah Dorweiler
文章来源:https://dev.to/rachc/identifying-the-dirt-in-our-code-third-party-tests-and-classes-5epd


