如何写出整洁的函数

Talk is cheap show me the code!

我想这句话充分表达了代码的重要性,而我们大部分的代码就是函数,整洁的函数优雅、高效,让人赏心悦目!它能够很容易的被修改、应该讲述事实,不引人猜测。为了写出整洁的函数,码农们一直在努力着、探索着、实践着,在这篇文章中,笔者结合自己多年的工作经验和其他大牛的一些文章,总结出了一些原则、模式,供大家参考与实践!

简洁函数的重要性

越简单的东西越容易理解,函数也是一样

写出简洁的函数一定是非常困难的,凡是写出那种一大堆让人读不懂的复杂函数的一定不是一个好的coder.

我们每次写代码要调用自己或别人写的函数的时候,是要看看具体的实现的,而如果该部分实现写的非常的晦涩难懂,那么在一定程度上也会影响我们的开发效率。

那么如何让写出的函数简洁呢?下面分别从以下几个方面介绍以下

取名字

命名无处不在,包名、类名、方法名、参数名、变量名等等。

好名字胜过千言万语!

名字要名副其实

要可以理解,不能模糊,要能表达具体的含义,如:

int i ; // 工作日

变量i什么也没说明,如果换成workDays是不是更好些?

避免误导

避免使用可能会引起误会的命名,如:

int pi

大家看到第一眼都以为是圆周率,但是实际上它是partIndex的缩写。

误导的另一种形式是相近的拼写。如:小写字母I和1,大写字母O和0。应当避免使用。

要有一定的区分度

避免使用a1,a2,……,aN这个样的命名,没有任何区分度。

user,userInfo,userData这几个命名同样是废话,也没有任何的区分度。

能读得懂的命名

一定是人类能够识别的语言,鸟语是看不懂的!

如:要表达一个交易时间的变量,可以命名为tradeymdhms,也可以命名为tradeDate。显然,人们更容易记住后者。(这也说明学好英语也是蛮重要的)

尽量用动词

方法名应是动词或动词短语,如:

void driveCar()

每个概念一个单词

给每个行为概念一个词,并一以贯之!这样当再次看到这个词的时候,就能大致明白是什么意思了。

如插入数据库记录,又是insert,又是save,又是put,这样很让人迷惑,所以用一个。

避免双关语

同一个单词,不同的概念,就是双关了!如:

  • 插入数据库记录 insert
  •  list中插入数据 insert

这个地方insert双关了,应该换一个,如:list中用append

优先技术性命名,其次为业务性命名

技术性命名是指技术领域内的名称,如:queue、pool等

因为只有程序员才会读你写的代码(机器除外)!

因此,在命名的时候,应该首先以技术性相关的词语来命名,如usersQueue等,其次才是业务性的命名

 语境

变量或命名应该有语境,这样才能自我说明。

如:name,单独放在这里没有什么意义,如果是userName,就有了意义,因为有user这个前缀作为语境。

不过,通常来说,不要添加没用的语境。比如user.userName,这里就显的很多余。

一些原则 

我们写的函数比写的类还要多,写好函数非常的重要,如何写好函数呢?

最重要原则:

短小

其实我也不知道为什么要短小,只是写过看过了那么多的代码,函数就应该短小!

那么到底多短小算短小呢?也没有一个完美的答案,但有一个标准:

 

函数中代码块的缩进层级不该多于一层或两层。

只做一件事情

每个函数只做一件事情。

一件事情不等于就是一个函数调用,处在同一个抽象层级上的就可以。

多了的,应该分解为更小的函数。

 

同一抽象层级

一个函数的函数体应该只包含同一抽象层级的代码。

理解起来比较难,下面举个例子啊

 

public void transfer(Account from,Account to,Money m){

withdraw(from,m);

deposit(to,m);

}

public void withdraw(Account account , Money m){

account.withdraw(m);

}

public void deposit(Account account , Money m){

account.deposit(m);

}

其中withdraw和deposit处于同一抽象层,因为它们都只是定义了一个概念:支取、存入。

而下面这样写,就不是一个抽象层级。

public void transfer(Account from,Account to,Money m){

withdraw(from,m);

account.deposit(m);

}

public void withdraw(Account account , Money m){

account.withdraw(m);

}

望大家自行体会!

switch语句

应该避免使用之,因为它首先违反了单一权责原则(SRP),因为有好几个要修改他的理由。

也违反了开放闭合原则(OCP),每当添加新类型时,就必须修改之!

那怎么避免呢?可以使用抽象工厂来 尽量避免 它的发生。当然还有一些其他的方法,如:反射等。

取个好名字

函数越短小、功能越集中、就越便于取个好名字。

另外,别害怕长名字,要比短而令人费解的名称好很多。 别害怕花时间取名字,你会发现你不是在取名字,而是在整理思路!

名称要与抽象层级相符合!

要注意参数的顺序,最好参数也是名字的一部分,例如:

transfer(from,to,withMoney)

封装条件、避免使用否定性条件

尽量把if,while等逻辑判断的条件封装到一个函数中。

且这个函数尽量是肯定式的,否定式的要不肯定式的难明白一些。

 

魔术数字 

单独出现的数字、字母等,让人无法理解,如:

 

if(status==1)

这个“1”是什么意思?

可以使用常量或枚举值的方式改造它,如:

 

if(status==ACTIVE)

尽量不要返回NULL

Null是很麻烦的,每次都要进行判断,导致了大量的恶心的代码。

尽量返回空对象,或抛出异常来解决。

 函数参数 

参数越少越好,最多不应该超过三个,不过还得具体问题具体分析。

参数越多,参数与参数之间的逻辑关系就越多,单元测试的组合就越多,越难以测试。

参数多的时候,可以将之封装为类。当然要把相同概念或问题域的封装在一起。

使用多个函数,要比使用一个函数多个参数要好一些!不过,这也通常说明,该函数不只做了一件事。

 flag参数 

flag是指一些标记,通常表示函数不只做一件事情,这个应该避免使用。

如:

transfer(Boolean useWX)

这个函数其实做了两件事,一个是通过微信转账,另一个是不通过微信转账。应该把其一分为二,即:transfer(),transferUseWX() 。

 尽量没有副作用 

应该是可以被重复调用的,而且尽量没有副作用。

尽量是幂等的。如转账,不会因为调用第二次而转了两次。

如果有副作用,在方法名中应该表达出来。如:createOrRetrunUser

 

查询与命令相分离 

一个函数要么是查询的,即:不修改任何数据!,要么就是命令的,即:做业务,修改数据!

 二者不可兼得

错误处理

错误处理就是一件事,应该是一个整体,而不是分散到各处的地方。

比如:在java等语言中,catch中的代码库就应该是一个函数!

 

别重复自己(DRY) 

重复很危险,他让你的思路变得混乱,修改起来麻烦,代码中散发着坏味道。

 总结

编写程序就是在讲故事,就是在把各个函数干净利落的拼装在一起,为此,应该:

取个好名字

短小的函数

抽象

DRY

DRY

DRY

本文文字及图片出自 微信公众号

余下全文(1/3)
分享这篇文章:

请关注我们:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注