内容简介:我们经常会遇到两个词,函数(Function)与方法(Method),简言之:他们的区别:方法底层实现本质还是函数,只是隐式传递了对象引用或指针,方法最终通过转化为函数的形式进行调用。为了简化后面的述诉,方法与函数统一称函数,不再区分。
函数与方法
我们经常会遇到两个词,函数(Function)与方法(Method),简言之:
- 函数不属于任何对象
- 方法是关联到对象内的函数
他们的区别:
- 函数是面向过程编程中,为解决问题划分每个步骤的体现
- 方法是面向对象编程中,对象能提供的能力或行为的体现
方法底层实现本质还是函数,只是隐式传递了对象引用或指针,方法最终通过转化为函数的形式进行调用。为了简化后面的述诉,方法与函数统一称函数,不再区分。
他们的必要性:
- 让代码可以重复使用,他们是”积木“
- 函数黑盒特性,有效封装,隐藏细节
短小的好处
Kent Beck
在 Smalltalk Best Practice Patterns
中定义的 Composed Method
模式。指出函数应该只能做一个层次上的抽象。函数应该是短小的,对于Smalltalk函数大小应该在一至六行。
如果是现代语言像Java/Go/Scala/Kotlin等,每个函数应该是控制在有效行数在 20到25 行 。短小的函数会带来如下好处:
- 维护成本:越长的函数越要花费更大的成本去了解它要做什么,以及在什么地方作修改。而对于小函数,你可以快速的查明应该在何处作修改(尤其是在应用测试驱动开发的时候)
- 代码可读性:在初始的学习曲线之后,小函数使得你更容易理解一个类要做什么。而且你不必滑动窗口就可以理解代码
- 拥有重用潜力:把函数分解成一些小的模块,你可以识别出代码中通用的抽象,通过使用这些通用的抽象使你的代码总量急剧下降
- 拥有子类化潜力:函数越长,你就越难以创建一个子类使用这个函数
- 命名:小的函数命名起来要容易一些,因为它做的事情少
- 性能属性:遇到了性能问题,一个使用composed method的系统更容易定位性能瓶颈
- 灵活性:小函数使得重构更加容易,并且容易找到设计缺陷,比如feature envy
- 代码质量:把大函数分解成小函数,定位隐晦的错误更加容易
- 最小化注释:虽然注释是很有价值的东西,大多数注释可以通过谨慎的命名和调整结构消除。代码已经能说清楚,则注释是没有必要的。
注:来自 Small Methods: Nine Benefits of Making Your Methods Shorter
影响因素
为了达成短小的函数,业界已有非常多的度量指标,指导我们写出可读性很好的函数。当然我们不能只是奔着降低指标而去,指标只是手段,不是目标。
圈复杂度
圈复杂度是一种衡量代码复杂程度的标准,由 Thomas J. McCabe, Sr.
提出。它用来衡量一个模块判定结构的复杂程度,数量上表现为独立的行路径条数。
圈复杂度高带来的害处有(建议平均值控制在15以内):
- 过高说明逻辑复杂不容易理解
- 逻辑复杂可能导致质量低下
- 路径过多难以测试覆盖
圈复杂度高主要表现:
- 分支语句多:if/else, switch/case, for, while
- 表达式复杂:条件表达式复杂,3个及以上逻辑运算符
降低圈复杂度方法:
- 抽取函数:独立业务代码封装为函数,通过函数名诠释代码作用,做到见名知意
- 合并重复:不同条件的分支,有相似的处理,可以提炼出分支以外,或者封装为函数
- 分解条件式:复杂的条件表达式,使用函数进行封装
- 移除控制标记:有控制标签作为条件的,使用break/return取代
- 设计模式:对于同一层的if/else, switch/case分支过多,可考虑采用状态机或策略模式、表驱动法(Map查找)
- 单一职责:一个函数应该只实现一个功能点
嵌套层次
不恰当的if/else与try/catch语句,是最为常见的嵌套层次过深代码;对于异步api,像nodejs还没有promise与async/await语言特性时,也会导致callback嵌套层次过深。
嵌套层次过深带来害处显而易见(建议层次不要超过4层):
- 圈复杂度变高,难以理解与测试
- 有时像”横放着的金字塔“,代码太长,不方法阅读
降低if/else嵌套层次方法:
if + return e = (a==b) ? c : d
try/catch可以再嵌套try/catch,但也是最容易出逻辑问题的地方,降低try/catch嵌套层次方法:
- 抽取函数:try与catch的中各代码块超过10行以上,建议是分别抽取函数,try/catch块内只有函数调用
- 合并try/catch:分析是否可以合并try/catch到同一层次
- 折分try/catch:让try的范围更小,尽早catch,避免大try中不必要的嵌套
我们会经常对数据进行操作,Java8引入Stream对象。若能合理使用它,也可以减少迭代(for语句–> map方法调用)、条件判断(if/else语句 –> filter方法调用)组合带来的嵌套。面向过程变成面向函数式编程,也会让你的代码更为的清爽。
参数个数
函数的输入参数过多,会使函数易于受外部的变化影响,从而导致函数变得不稳定,代码维护困难。过多的控制标记参数也会导致参数的使用组合变多,代码的分支路径变多,也就增加了测试的工作量。
参数个数多少个才算多?建议是不超过5个。
是否要将参数封装成对象,不能只看参数的数量,还要看它的业务意义,有时封装成对象反倒增加了阅读成本。参数过多说明它依赖过多,我们需要考虑是否需要对函数进一步拆分。
对于构造方法参数过多,我们可以采用 链式调用
,它是一种Builder模式,比一堆的参数列表调用更有意义,也不容易出现赋值顺序出错而导致问题。
变量个数
函数局部变量个数多,是函数的逻辑实现需要依赖较多的外数或内部数据,依赖多则会导致复杂度增加。
局部变量个数多少个才算多?建议是不超过5个。
解决局部变量变多的另一种思路是 Replace Method with Method Object
(来自重构一书),以函数对象取代函数,做法是将函数放进一个单独的对象当中,使用这个单独对象的值域(filed)来替代原函数中的局部变量。若这个单独的对象使用的范围非常小,我们通过把它声明为内部静态类。
结语
千里之堤溃于蚁泬,短小精悍的函数,是构建健壮的软件基石。千里之行始于足下,从编写短小精悍的函数开始。
以上所述就是小编给大家介绍的《编写短小的函数/方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- [译] 实用指南: 编写函数式 JavaScript
- 在Clojure中编写累加器函数
- [译] 编写函数式的 JavaScript 实用指南
- 如何通过 JavaScript 编写高质量的函数(三):函数式编程之理论篇
- 如何通过 JavaScript 编写高质量的函数(四):函数式编程之实战篇
- 如何在Haskell中编写一个恒定空间长度函数?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入应用C++11
祁宇 / 机械工业出版社 / 2015-5 / 79
在StackOverflow的最近一次世界性调查中,C++11在所有的编程语言中排名第二, C++11受到程序员的追捧是毫不意外的,因为它就像C++之父Bjarne Stroustrup说的:它看起来就像一门新的语言。C++11新增加了相当多的现代编程语言的特性,相比C++98/03,它在生产力、安全性、性能和易用性上都有了大幅提高。比如auto和decltype让我们从书写冗长的类型和繁琐的类型......一起来看看 《深入应用C++11》 这本书的介绍吧!