前几天在“TDD和重构练功房”中看到熊老师发了一篇文章叫做"长函数,有如王大妈的裹脚布",这篇文章认为长函数是一个 bad smell,同时对函数长短的标准进行了量化:
一个常见的原则是将方法的行数控制在 5 行之内——《软件开发沉思录》
5 行之内这一标准简直让我感到恐怖,对,不是惊讶,真的是恐怖的感觉。 5 行以内的描述空间,这怎么可能,稍微复杂一点的函数何止几十行...,深呼吸,冷静一下,再想想,这一规则背后到底还有什么我不知道的,于是就有了以下对话:
lyning(我):我没看错吧,居然不是 5 行左右,是 5 行之内?
刘光聪:@lyning 这很正常,这几年遇到的项目(新项目,ai领域)大多都控制在5行以内。都是在@熊节 2011年洗脑之后开始实践的。
lyning(我):感觉控制函数行数少是一种使代码逼近单一职责的手段。
熊节:单一职责和恰当的抽象层次。
熊节:麦肯锡方法,讲事情永远是3-5个点。
lyning(我):熊老师,恰当的抽象层次,怎么理解这句话呢?
熊节:就…比如我跟你讲一个事,我知道这个事1分钟讲不完,要讲5分钟这么个复杂度
熊节:我就一定会分成一二三点,每一点我会强调一下,再具体讲里面的内容,讲完以后我会总结一下我讲了三个点。
熊节:跟一个几十行的函数是一样的。
lyning(我):有点明白了,把大问题要拆分成小问题,拆分出来的这几个小问题都属于这个大问题层面的东西。
熊节:仝老师有个套路,就是每个问题你始终把它描述成 输入-处理-输出 三句话.
刘光聪:
刘光聪:一个小的c++例子,抽象层次分明。
...
从上面可以对话中可以得到以下关键字:
- 单一职责
- 恰当的抽象层次
熊老师采用比喻描述了什么是恰当的抽象层次,刘老师可视化了该概念在代码中的体现。可以看到一个 10 多行的代码被重构到 5 行秒懂的函数,看来这个标准还是可行的嘛。
其实该原则出自《软件开发沉思录》对象健身操一节,更“恐怖”的是,对象健身操还列出 9 个堪称“变态”的原则:
- 方法只使用一级缩进(One level of indentation per method)
- 拒绝使用else关键字(Don’t use the ELSE keyword)
- 封装所有的原生类型和字符串(Wrap all primitives and Strings)
- 一行代码只有一个“.”运算符(One dot per line)
- 不要使用缩写( Don’t abbreviate)
- 保持实体对象简单清晰(Keep all entities small)
- 任何类中的实例变量都不要超过两个(No classes with more than two instance variables)
- 使用一流的集合(First class collections)
- 不使用任何Getter/Setter/Property(No getters/setters/properties)
还能更短
函数的第一规则是短小。第二条原则是还要更短小。
函数应该只做一件事。做好这件事。只做这件事
上面的描述出自 Bob 大叔的《代码整洁之道》,对于函数应该多小,Bob 大叔提到了他看了 Kent Beck 写 的一个程序,函数都是只有三到四行,非常简洁易懂。最终 Bob 大叔对函数应该多长下了一个定义,他给出了一个只有 3 行函数体代码的示例,表示函数就应该这么短。这又一次颠覆了我的价值观!
单一职责
单一职责是 SOLID 中的首要原则,通过它可以引出其它原则。可以发现函数越短,那么它能做的事情就越少,就越能做好这件事情,也就越能驱动出单一职责的函数。
恰当的抽象层次
对于 add()
这个函数而言,if (readonly)
、 atCapacity()
、grow()
和 addElement()
这 4 个逻辑体现了同一抽象层次,因此 add()
函数还是只做一件事,毕竟编写函数是为了把一些大的概念拆分成另一个抽象层次的一系列步骤 。
如果函数体现了非常多的细节,或者处于不同的抽象层次,这对于读者来说简直是灾难,不仅如此,乱其实还暗示了作者在当下同样也是思维混乱。
编程与思维
软件开发就像是一个口耳交替的游戏,获取准确的一手信息不容易,把问题描述清楚同样也不容易。笔者现在觉得编程是一项非常重视描述技巧的活动,表达得是否清楚很大一部分源于人的思维是否清晰(结构化),也许编程只是基本功,思维才是内功,毕竟把问题搞复杂,比弄清晰要简单得多。
良好的描述技巧
要把逻辑抽象化,必须知道某段逻辑在做什么,并且对函数进行恰当的描述,这里的难点之一在于它需要良好的描述技巧,好在描述技巧其实也有一些模式和规律可循,厉害的人已经帮我们提炼了部分模式,可以参考“名正则言顺:聊聊“起名”这件事”
秒懂
上面谈了一些模模糊糊的概念,而秒懂应该是短函数的一个最直观的体现。不过不仅仅让自己秒懂,还要让读者秒懂,因此写好了代码,应该分(e)享(xin)给其他的小伙伴,看看小伙伴们的反馈,在反馈中不断改善代码。
如果还觉得不过瘾,那再看看这个函数,给自己计时看看需要多长时间读懂:
然后再看看这个:
总结
笔者认为,短函数的实际价值在于容易维护。处于同一抽象层次的短函数,做的事情越少,需要被理解的概念也就越少,就越不容易出现认知超载。需要强调的是,短函数、恰当的抽象层次和良好的描述,对于大部分人来说,不是一下子就冒出来的,而是被打磨出来的,怎么打磨呢?当然是 TDD。
欢迎关注我的微信订阅号,我将会持续输出更多技术文章,希望我们可以互相学习。