代码整洁之道读书笔记——第三章:函数

第三章 函数

作者将一个复杂的函数提炼为9行代码,告诉了我们什么样的代码易于阅读

3.1 短小

作者的建议是一个函数封顶20行最佳,每个函数之说一件事,每个函数依序把你带入下一个函数。

if、else、while语句等,其中的代码应该只有一行,大概是一个函数的调用语句。调用函数应具有比较有说明性的名称,这样也能增加一定的文档的价值。

我这么理解:20行封顶、函数带入函数、控制代码块中的行数、控制缩进。

我们可能无法做到作者说的if、else、while语句的代码块只有一行,缩进层级只是一层或者两层,但是一定要注意保持代码块和缩进不要太夸张。

3.2 只做一件事

书中大致的提到了这么一个例子,这个函数做了一件事,但是也很容易看做是三件事

如果函数只是做了该函数名下同一抽象层上的步骤,则该函数还是做了一件事

编写函数的目的是为了把大一些的概念拆分为另一抽象层的一系列步骤

我们不应该把不同抽象层级的代码都堆到一起

public static String xxx() {
    if (isTestPage) {
       // 另外处理数据
    }    
    return xx
}

以我的理解,举个例子:点击了列表某一项的审核按钮,显示一个审核弹框

public void onClickReviewBtn() {
    // 获取某一项的item信息
    // 获取某一个用户的权限信息
    // 根据item和权限的信息,去显示不同的审核弹框
}

以上三个步骤应该是三个方法,而不是三大段代码

如何获取item的信息,以及获取以后要不要做一些处理。这是另一个抽象层上的步骤

同理,如何获取用户的权限信息,是直接从内存中获取,还是说从本地获取,我们不需要关心

同理,最终根据item和权限的信息,有可能去显示几种审核弹框,也是另一个抽象层上的步骤

 

判断函数是否不止做了一件事,可以看是否能再拆出一个函数

该函数不仅只是单纯的重新诠释其实现

比如没必要再拆出一个onClickReviewBtnIfAdminUser()

3.3 每个函数一个抽象层级

如果一个函数有很多不同抽象层的概念,那这个函数是一个烂函数

自顶向下读代码:向下规则

有自顶向下的阅读顺序,由上一个函数引出下一个函数

这个抽象层级的问题应该是比较好理解的

3.4 switch语句

首先不要在switch语句中有太多的代码,最好只有一行代码,而且这一行应该处于较低抽象层级,比如利用多态来创建对象

对于switch语句和if/else下类似的代码:

public Money eat (Employee e) {
   if (e.type == COMMISSIONED) {
        return calculateCommissionedPay(e);
   } else if (e.type == HOURLY) {
        return calculateHourlyPay(e);
   } else if (e.type == SALARIED) {
        return calculateSalariedPay(e);
   }
}

或:

public void eat (User user) {
    switch (e.type) {
        case COMMISSIONED:
            return calculateCommissionedPay(e);
        case HOURLY:
            return calculateHourlyPay(e);
        case SALARIED:
            return calculateSalariedPay(e);
        default:
    }
}

出现的问题:

  1. 它太长,出现新的雇员类型时,还会更长
  2. 明显做了不止一件事
  3. 违反了单一职责,有好几个修改它的理由
  4. 违反了开闭原则,添加新类型时,必须修改此方法
  5. 最最麻烦的就是整个系统中可能到处出现这种结构的函数

我们在做那种多身份的app的时候,比如一个app有三种身份,但是这三种身份看每个页面还都不一样,如果不选择多态的写法,就会在每个页面都会出现这种类似if/else的三个身份的判断,一旦当你加入第四种身份的时候,整个app所有涉及到身份判断的页面都需要去修改,那这时候说明我们的代码写的出问题了。

我们应该去抽象出一个类,然后多态实现,通过工厂去创建这些对应的对象,在创建的时候难免也一定会通过if/else和switch语句来实现多态,但是这个是避免不了的,我们已经把它控制在了一个低抽象的层级。如果在此时加入第四种身份,我们只需要去修改工厂中创建雇员的方法,并且新建一个对象去继承抽象雇员,并实现其所有的方法即可。

3.5 使用描述性的名称

给函数取个好名称很重要,别害怕取长名称,长而具有描述性的名称,要比短而令人费解的名称好,也比描述性的长注释更好

命名方式要保持一致,某一含义只是用某一个单词,并且要注意单词与单词之间的顺序

3.6 函数参数

参数越少越好,三参数函数就是我们有点无法接受的了,除非你有足够的理由

3.6.1 一元函数的普遍形式

输入一个参数,然后输出另一个参数,可能是输入的这个参数也可能是其他对象

有输入参数无输出参数,像是一个事件,干了某一件事,或者对输入的参数进行了某些操作

如果对输入的参数进行某些修改和转换,结果就应该体现在返回值上

StringBuffer transform(StringBuffer in) 要比 void transForm(StringBuffer out)强

3.6.2 标识参数

向方法中传入boolean值有些丑陋不堪,大部分的时候我们应该把函数一分为二

3.6.3 二元函数

有时候确实两个参数就正好,比如new Point(0, 0)代表了xy,如果看到new Point(0),会感觉很惊讶

有些时候两个参数没有自然的顺序,经常会搞错

如果二元函数传入了一个对象,我们可以把这个函数写成次对象的成员之一,从而无需传递

或者说直接当成一个成员变量,或者分离一个新类,然后在构造器中传入参数

3.6.4 三元函数

尽量就不要写三元函数了,不过我们做android的常见的是canvas的那几个绘画参数,那个因为矩形坐标什么的,可能会有六七个参数,这个也算是正常的

3.6.5 参数对象

如果函数需要两个、三个或三个以上的参数,就说明可能一些参数应该封装成类了

3.6.6 参数列表

注意如果参数列表是可变的,如果传入太多,超过三元就要注意犯错了

3.6.7 动词与关键字

给函数去个好名字,write(name)可能比writeField(name)更好一些

3.7 无副作用

书中作者举了一个检查密码的函数,在检查密码成功后却出现了初始化会话的操作,但是在函数名并没有告诉我们会有初始化会话的操作,这样就有可能会出现误调用,会话数据可能会丢失。

所以一定要注意函数的命名告诉我们到底干了什么

参数大多数会被认为是输入而非输出

如果你想对一个StringBuffer添加footer,不要写成appendFooter(s);

而应该写成s.appendFooter();

尽量避免使用输出参数,当然这个因情况而定,有时候也确实是需要去处理一下网络请求回来的数据,可能也会修改函数中传入的参数对象中的属性

3.8 分割指令与询问

函数要么干什么事,要么回答什么事

比如 public boolean set(String name),这个函数就是很混乱,我们要避免出现这种问题的发生

3.9 通过异常替代返回错误码

3.9.1 抽离Try/Catch代码块

try/catch主题的代码块应该抽离出一个函数,这个应该毋庸置疑,跟前面说的if/else、switch、while中的代码尽量只有一行是一个道理

3.9.2 错误处理就是一件事

如果try再某个函数存在,它就应该是这个函数的第一个单词,在catch/finally代码后也不应该有其他内容

3.9.3 Error.java依赖磁铁

返回错误码通常暗示某处有个类或者枚举,定义了所有错误码,如果对枚举进行修改时,所有这些类都需要重新编译和部署,这对枚举类造成了负面压力。

这个重新编译和部署其实还是可以接受的,很多第三方的SDK也都是使用错误码,当然也有使用新的异常来告诉我们出现了错误

使用异常代替错误码,新异常就可以从异常类派生出来,无需重新编译

3.10 别重复自己

这个问题其实在我们刚做项目的时候就会遇到,一定不要重复,不然会显的很低级,很low

如果一段代码被两个以上的地方调用,我们可以考虑是否可以把它们封装成一个方法

在写if/else的时候,我们考虑是否写了重复的判断,与或非我们用的合理吗?

3.11 结构化编程

小函数中出现偶然出现的return、break、continue语句没有坏处

goto语句尽量避免使用

我感觉goto语句应该是已经被废弃了吧

3.12 如何写出这样的函数

作者说他写代码一开始冗长而复杂,有很多缩进和嵌套循环,有过长的参数列表,名字随意取,有随意的代码。会在之后去打磨这些代码,分解函数、修改名称、消除重复等。

他并不从一开始就按照规则写函数,他想没有人能够做到

我个人认为确实也是这个道理,没有人能保证写出来就很完美,我们看到别人的代码短小又精悍,不知道修改了多少次才修改成这个样子的。

3.13 小结

我挺赞同这里面的一句话:

大师把系统当故事来讲,而不是当程序来写

他们使用选定编程语言提供的工具构建一种更为丰富且表达力更强的语言,用来讲这个故事

 

读完这一节很有收获,尽管我觉得我以前的代码写的已经够好了,但是还是有很多要注意的地方,当然在实战开发中不可能达到作者所说的那种变态的程度,但是我们应该注意我们的粒度,掌握好那个度是最重要的

要记住,我们的函数应该短小,有个好名字,并且被很好的归置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值