高质量代码的特征

简介: 高质量代码的特征

回想起来,我觉得我们似乎在误读Uncle Bob的Clean Code,至少我们错误地将所谓Clean与可读性代码简单地划上了等号。尤为不幸的是,在Clean Code一书中,从第二章到第五章都围绕着可读性代码做文章,于是加深了这种错误的印象。

许多具有代码洁癖的程序员将代码可读性视为神圣不可侵犯的真理,并奉其为高质量代码的最重要特征,封上了“神坛”。殊不知,Uncle Bob在Clean Code的第一章就通过别人之口对所谓“Clean Code”进行了正名:所谓整洁代码并非仅仅是“清晰”这么简单。

按照Kent Beck的简单设计规则,排在第一位的其实不是可读性,而是“通过所有测试”。其中潜藏的含义是满足用户正确的需求,因为测试可以看做是用户提出的需求。这个需求不仅仅是业务上的,还包括质量属性的需求,例如性能、安全等属性。

消除重复和提高表达力这两点,有时候会互相促进,去除了冗余的代码,会让代码变得更加清晰;然而,有时候却又互相冲突,消除重复的成本可能会比较高,导致提取了太多细碎微小的实体,反而增加了阅读障碍。

故而我常常将Uncle Bob提出的“函数的第一规则是要短小。第二条规则是还要更短小。”看做是一种矫枉过正的强迫。对于那种喜欢编写大函数的程序员而言,确实需要时刻铭记这一原则,但切记不要将其视为最高准则。保证函数短小是有前提的,仔细阅读Kent Beck的简单设计原则,依其重要顺序:

能通过所有测试;

没有重复代码;

体现设计者的意图;

若无必要,勿增实体(方法、函数、类等)。

如果程序满足了客户需求,没有重复代码,函数的表达已经足够清晰地体现设计者意图,为何还要不断地提取函数,使得函数变得极为短小呢?真正有意义的原则是“让函数只做一件事情”。

正因为此,在Clean Code书中,Uncle Bob展示的对FitNesse中HtmlUtil.java的第二次重构并无必要。在经过第一次重构后,代码如下所示:

public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) throws Exception {
    boolean isTestPage = pageData.hasAttribute("Test");
    if (isTestPage) {
        WikiPage testPage = pageData.getWikiPage();
        StringBuffer newPageContent = new StringBuffer();
        includeSetupPages(testPage, newPageContent, isSuite);
        newPageContent.append(pageData.getContent());
        includeTeardownPages(testPage, newPageContent, isSuite);
        pageData.setContent(newPageContent.toString());
    }
    return pageData.getHtml();}

这段代码的结构与层次已经非常清晰,也对实现细节做了足够合理的封装与隐藏。若要说不足之处,或许可以将如下代码再做一次方法提取,使其满足SLAP原则(单一抽象层次原则):

newPageContent.append(pageData.getContent());
//提取为:includeTestContents(testPage, newPageContent)
而Uncle Bob做的第二次重构,除了将方法变得更加短小,隐藏了太多细节从而引入更多层次之外,究竟给代码的清晰带来了什么呢?
public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) throws Exception {
    if (isTestPage(pageData))
        includeSetupAndTeardownPages(pageData, isSuite);
    return pageData.getHtml();}

过犹不及啊!

有时候,为了去除重复,就必须要从相似代码中寻找到一种模式或者某种抽象,进而对其进行提取。过分的提取反而会让代码变得很难阅读,这是因为提取的手段常常会引入“间接”。正如Martin Fowler所说:“间接性可能带来帮助,但非必要的间接性总是让人不舒服”。不必要的间接常常妨碍代码的直截了当和干净利落。倘若去除重复带来的唯一好处仅仅是避免一个类中少许的私有重复,去除这样的重复其实意义真的不大。

我喜欢清晰的代码,但我认为保持代码的正确、健壮与高效同样重要。

因为代码洁癖的缘故,我曾经将大量的非空判断、非法检查与异常处理视为干扰清晰代码的洪水猛兽,但如果不做这些“脏活累活”,代码就可能变得不健壮。在Java中,若真要避免这些判断,可以考虑转移职责,通过定义Checked Exception,将异常处理的职责转移给方法的调用者。然而,职责的盲目转移始终是不负责任的。实现每个方法和每个类的程序员应该保证自己的代码是自治的。

如下代码:

@Overridepublic void run() {
   if (isFromFile) {
        if (hasQuery) {
            throw new RuntimeException("both --execute and --file specified");
        }
        try {
            query = Files.toString(new File(clientOptions.file), UTF_8);
            hasQuery = true;
        }
        catch (IOException e) {
            throw new RuntimeException(format("Error reading from file %s: %s", clientOptions.file, e.getMessage()));
        }
    }}

这样的代码确实谈不上优雅,然而足够充分的判断保证了代码的正确性与健壮性。我只能说,在满足了这两点的前提下,可以聪明地利用诸如防御式编程、Optional来规避多余的嵌套或分支,从而提高代码的可读性。

Effective Java总结了高质量代码的几个特征:清晰、正确、可用、健壮、灵活和可维护。我认为这一总结非常中肯。写代码真的不要太偏执,不分任何场景一味地追求代码的可读(清晰),一味地重申DRY,我觉得都是不负责任的态度。

或许是我老了的缘故,我变得不再理想主义;但更多的原因是因为我看到太多追求所谓“整洁代码”的程序,不愿考虑复杂繁琐的异外情况从而导致程序的不健壮;因为去除重复带来的不必要间接影响了代码的简洁与干净,甚至影响了代码运行的性能。

整洁代码是必须的,但不是衡量代码质量的唯一标准!

相关文章
|
5月前
|
机器学习/深度学习 存储 算法
Transformer、RNN和SSM的相似性探究:揭示看似不相关的LLM架构之间的联系
通过探索大语言模型(LLM)架构之间的潜在联系,我们可能开辟新途径,促进不同模型间的知识交流并提高整体效率。尽管Transformer仍是主流,但Mamba等线性循环神经网络(RNN)和状态空间模型(SSM)展现出巨大潜力。近期研究揭示了Transformer、RNN、SSM和矩阵混合器之间的深层联系,为跨架构的思想迁移提供了可能。本文深入探讨了这些架构间的相似性和差异,包括Transformer与RNN的关系、状态空间模型在自注意力机制中的隐含作用以及Mamba在特定条件下的重写方式。
222 7
Transformer、RNN和SSM的相似性探究:揭示看似不相关的LLM架构之间的联系
|
5月前
|
机器学习/深度学习 自然语言处理
如何让等变神经网络可解释性更强?试试将它分解成简单表示
【9月更文挑战第19天】等变神经网络在图像识别和自然语言处理中表现出色,但其复杂结构使其可解释性成为一个挑战。论文《等变神经网络和分段线性表示论》由Joel Gibson、Daniel Tubbenhauer和Geordie Williamson撰写,提出了一种基于群表示论的方法,将等变神经网络分解成简单表示,从而提升其可解释性。简单表示被视为群表示的“原子”,通过这一分解方法,可以更好地理解网络结构与功能。论文还展示了非线性激活函数如何产生分段线性映射,为解释等变神经网络提供了新工具。然而,该方法需要大量计算资源,并且可能无法完全揭示网络行为。
56 1
|
8月前
|
设计模式 算法 程序员
编码之舞:探索编程艺术的深层美学
在数字世界的无限画布上,代码是精确而生动的笔触。本文将深入编程的核心,探讨如何将技术与艺术融合,挖掘编程过程中的创造性与美学价值。通过分析编程语言的设计哲学、算法的优雅以及软件工程中的创新实践,揭示编程不仅是逻辑与功能的实现,更是一场思维与美的交响舞蹈。
|
7月前
|
机器学习/深度学习 算法 搜索推荐
代码之舞:探索编程艺术的深层美学
在数字世界的舞台上,编程不仅是技术的体现,更是艺术的一种展现。本文将深入探讨编程背后的艺术性,从算法的优雅到代码的简洁,揭示如何通过技术实现创造性思维的飞跃。我们将一起走进编程的世界,感受它在解决问题过程中所展现出的独特魅力和美学价值。
|
9月前
|
存储 安全 关系型数据库
技术人必修课:利用金字塔原理高效思考与表达
作者写这篇文章的目的就是希望能够帮助更多同学了解金字塔原理并合理应用,不只是写作,更是要着眼于思考和表达。本文将围绕认识金字塔结构、表达的逻辑、思考的逻辑、解决问题的框架、演示的逻辑这几个方面带领大家深入学习金字塔原理。
|
算法
基于模态凝聚算法的特征系统实现算法的自然激励技术(Matlab代码实现)
基于模态凝聚算法的特征系统实现算法的自然激励技术(Matlab代码实现)
146 0
|
9月前
|
Java 测试技术 程序员
2024提升计划|优秀程序员的10大共性特征
在设计代码时,很多开发者并没有考虑到"容易测试"这个因素,可测试性不强。如果工程师在开发逻辑的过程中同时考虑如何轻松地进行测试,那么编写出的代码将具备良好的可读性和简单性,并经过精心设计,而不仅仅是"能工作"而已。测试所得到的主要好处发生在考虑测试和编写测试的时候,而不是在运行测试的时候!
|
机器学习/深度学习 自然语言处理 算法
预测蛋白质间相互作用更准确、更细致,一个基于基因本体术语集的Transformer框架
预测蛋白质间相互作用更准确、更细致,一个基于基因本体术语集的Transformer框架
143 0
预测蛋白质间相互作用更准确、更细致,一个基于基因本体术语集的Transformer框架
|
机器学习/深度学习 算法 Python
【CEEMDAN-CNN-LSTM】完备集合经验模态分解-卷积神经长短时记忆神经网络研究(Python代码实现)
【CEEMDAN-CNN-LSTM】完备集合经验模态分解-卷积神经长短时记忆神经网络研究(Python代码实现)
168 0
|
机器学习/深度学习 算法 Python
【CEEMDAN-VMD-GRU】完备集合经验模态分解-变分模态分解-门控循环单元预测研究(Python代码实现)
【CEEMDAN-VMD-GRU】完备集合经验模态分解-变分模态分解-门控循环单元预测研究(Python代码实现)
421 0