函数');" target="_self">函数是实现程序功能的基本单位,每一个程序都是由一个个基本的函数构成的。写好一个函数是提高程序代码质量关键的一步。本文函数的编写,从函数命名,代码分布,技巧等方面入手,谈谈如何写好一个可读性高、易维护,易测试的函数。
  命名
  首先从命名说起,命名是提高可读性的第一步。如何为变量和函数命名一直是开发者心中的痛点之一,对于母语非英语的我们来说,更是难上加难。下面我来说说如何为函数命名的一些想法和感受:
  采用统一的命名规则
  在谈及如何为函数取一个准确而优雅的名字之前,首先重要的是要有统一的命名规则。这是提高代码可读性的基础的准则。
  帕斯卡命名法和驼峰命名法是目前比较流行的两种规则,不同语言采用的规则可能不一样,但是要记住一点:保持团队和个人风格一致。
  1、帕斯卡命名法
  帕斯卡命名法简单地说是:多个单词组成一个名称时,每个单词的首字母大写。比如:
  1 public void SendMessage ();
  2 public void CalculatePrice ();
  在C#中,这种命名法常用于类、属性,函数等等,在JS中,构造函数也推荐采用这种方式命名。
  2、驼峰命名法
  驼峰命名法和帕斯卡命名法很类似,多个单词组成一个名称时,第一个单词全部小写,后面单词首字母大写。比如:
  1 var sendMessage = function () {};
  2 var calculatePrice = function () {};
  驼峰命名法一般用于字段、局部变量、函数参数等等。,在JS中,函数也常用此方法命名。
  采用哪种命名规则并不,重要的是要遵守团队约定,语言规范。
  尽可能完整地描述函数所做的所有事情
  有的开发者可能觉得相较于长函数名来说,短函数名看起来可能更简洁,看起来也更舒服。但是通常来说,函数名称越短其描述的意思越抽象。函数使用者对函数的第一印象是函数名称,进而了解函数的功能,我们应该尽可能地描述到函数所做的所有事情,防止使用者不知道或误解造成潜在的错误。
  举个例子,假设我们做一个添加评论的功能,添加完毕后并返回评论总数量,如何命名比较合适呢?
  1 // 描述不够完整的函数名
  2 var count = function addComment() {};
  3
  4 // 描述完整的函数名
  5 var count = function addCommentAndReturnCount() {};
  这只是简单的一个例子,实际开发中可能会遇到得更多复杂的情况,单一职责原则是我们开发函数要遵守的准则,但是有时候无法做到函数单一职责时,请记得函数名应该尽可能地描述所有事情。当你无法命名一个函数时,应该分析一下,这个函数的编写是否科学,有什么办法可以去优化它。
  采用准确的描述动词
  这一点对母语非英语的开发者来说应该是比较难的一点,想要提高这方面的能力,主要的还是要提高词汇量,多阅读代码积累经验。
  这里简单说说我自己的一些感想和看法:
  1、不要采用太抽象广泛的单词
  很多开发人员会采用一个比较宽泛的动词来为函数命名,典型的一个例子是get这个单词。我们平时开发中经常会通过各种不同的方式拿到数据,但是每一种方式都用get有点太抽象了。具体如何命名,要具体分析:
  (1)简单的返回数据
  1 Person.prototype.getFullName = function() {
  2     return this.firstName = this.lastName;
  3 }
  (2)从远程获取数据
  1 var fetchPersons = function () {
  2     ...
  3     $.ajax({
  4     })
  5 }
  (3)从本地存储加载数据
  1 var loadPersons = function () {};
  (4)通过计算获取数据
  1 var calculateTotal = function () {};
  (5)从数组中查找数据
  1 var findSth = function (arr) {};
  (6)从一些数据生成或得到
  1 var createSth = function (data) {};
  2 var buildSth = function (data) {};
  3 var parseSth = function(data) {};
  这是一个简单的例子,我们平时开发中遇到的情况肯定会复杂得多,关键还是靠单词的积累,多阅读源码
  下面是整理的一些常用的对仗词,大家可以参考使用
  1 add/remove        increment/decrement       open/close
  2 begin/end            insert/delete                      show/hide
  3 create/destory    lock/unlock                        source/target
  4 first/last              min/max                             star/stop
  5 get/put                next/previous                     up/down
  6 get/set                old/new
  根据不同项目和需求制定好命名规则
  这一点也是很重要的,尤其是在团队合作中,不同的项目和需求可能导致的不同的命名规则。
  比如我们通常采用的命名规则是动宾结构,也是动词在前,名词灾后。但是有一些项目,比如数据接口等项目中,有的团队会采用名字在前,动词在后的形式,例如:
  1 public static Product[] ProductsGet(){};
  2 public static Product[] ProductsDel(){};
  3 public static Customer[] CustomerDel(){};
  4 public static Customer[] CustomerDel(){};
  这种的好处是看到前面的名词,比如ProductsGet,能很快的知道这是产品相关的数据接口。
  当然这个并不是的,关键还是要团队共同制定和遵守同一套命名规则。
  函数参数
  函数使用者在调用函数时,必须严格遵守函数定义的参数,这对函数的易用性,可测试性等方面都是至关重要的。下面我从几个方面来谈谈关于如何优化好函数参数的一些想法。
  参数数量
  毫无疑问,函数参数越多,函数的易用性越差,因为使用者需要严格眼中参数列表依次输入参数,如果某个参数输错,将导致不可意料的结果。
  但是,函数参数一定越少越好吗?我们来看看下面的例子:
  1 var count = 0;
  2 var unitPrice = 1.5;
  3 ....
  4 ...
  5 var calculatePrice = function () {
  6     return count * unitPrice;
  7 }
  在这个例子中,我们通过calculatePrice这个函数来计算价格,函数不接收任何参数,直接通过两个全局变量unitPrice和count进行计算。这种函数的定义对使用者来说非常方便,直接调用即可,不用输入任何参数。但是这里可能会有潜在的bug:全局变量可能在其他地方被修改成其他值了,难以进行单元测试等等问题。所以,这个函数可以传入数量和价格信息:
  1 var calculatePrice = function(count, unitPrice) {
  2     return count * unitPrice;
  3 }
  这种方式下,函数使用者在使用时,要传入参数进行调用,避免了全局变量可能存在的问题。另外也降低了耦合,提高了可测试性,在测试的时候不必依赖于全局变量。
  当然,在保证函数不依赖于全局变量和测试性的情况下,函数参数还是越少越好。《代码大全》中提出将函数的参数限制在7个以内,这个可以作为我们的参考。
  有的时候,我们不可避免地要使用超过10个以上函数,在这中情况下,我们可以考虑将类似的参数构造成一个类,我们来看看一个典型的例子。
  我相信大家平时一定做过这样的功能,列表筛选,其中涉及到各种条件的筛选,排序,分页等等功能,如果将参数一个一个地列出来必定会很长,例如:
  1 var filterHotel = function (city, checkIn, checkOut, price, star, position, wifi, meal, sort, pageIndex) {}
  这是一个筛选酒店的函数,其中的参数分别是城市,入住和退房时间,价格,星级,位置,是否有wifi,是否有早餐,排序,页码等等,实际的情况可能会更多。在这种参数特别多的情况下,我们可以考虑将一些相似的参数提取成类出来:
  1 function DatePlace (city, checkIn, checkOut){
  2     this.city = city;
  3     this.checkIn = checkIn;
  4     this.checkOut = checkOut
  5 }
  6
  7 function HotelFeature (price, star, position, wifi, meal){
  8     this.price = price;
  9     this.star = star;
  10     this.position = position;
  11     this.wifi = wifi;
  12     this.meal = meal;
  13 }
  14
  15 var filterHotel = function (datePlce, hotelFeature, sort, pageIndex) {};
  将多个参数提取成对象了,虽然对象数量增多了,但是函数参数更清晰了,调用起来也更方便了。