命名(code)

命名,顾名思义,就是对代码,即变量,类,等进行命名。

要想出一个简洁又准确的名字不是那么容易的。

正确的命名能帮你写出好的代码。如果不能为一个变量或者函数想出一个清晰又简洁的名字,那可能是你的代码有问题。那样的话,你就得考虑重写或者进行代码重构了。

有人说,有注释,其实,注释的目的是为了弥补代码自身在表达上的不足。

网摘

列举例子,说明什么是“不好的代码”,什么是“清楚的代码”

命名要能揭示意图

如何命名,在编程中这永远都是个老大难问题。有些程序员喜欢简化、缩短或加密名称,使得只有他们自己能懂。下面让我们看一些例子:

不好的代码:

1
2
3
4
5
int d;
// 天数
int ds;
int dsm;
int faid;

“d”可以表示任何东西。作者使用注释来表明他的意图,却没有选择用代码来表示。而“faid”很容易导致误解为ID。

清楚的代码:

1
2
3
4
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

命名时避免含义引起误解的信息

错误的信息比没有信息更糟糕。有些程序员喜欢“隐藏”一些重要信息,有时候他们也会写出一些让人误解的代码。

不好的代码:

1
2
Customer[] customerList;
Table theTable;

变量“customerList”其实不是个list。它是一个普通的array(或客户集合)。除此之外,“theTable”是一个Table类型的对象(你可以用IDE容易的发现它的类型),“the”这个词是个不必要的干扰。

清楚的代码:

1
2
Customer[] customers;
Table customers;

命名要有合适的长度

在高级编程语言中,变量名的长度通常不太限制。变量名几乎可以任何长度。虽然如此,这也可能使代码变得闹心。

不好的代码:

1
2
var theCustomersListWithAllCustomersIncludedWithoutFilter;
var list;

好的名称应该只含有必要的词汇来表达一个概念。任何不必要的字词都会使名称变长、难于理解。名称越短越好,前提是能在上下文中表达完整的意思(下订单这个场景中,“customersInOrder” 要比 “list” 好)。

清楚的代码:

1
2
var allCustomers;
var customersInOrder;

命名时编码规范保持一致,让规范帮助理解代码

所有的编程技术(语言)都有自己的“风格”,叫做编码规范。程序员应该在写代码时遵循这些习惯,因为其他的程序员也知道这些,并按这种风格编写。下面我们看一个没有明显规范的不好的代码例子。下面的这段代码没有遵循很好的已知的“编码规范”(比如PascalCase, camelCase, Hungarian规范)。更糟糕的是,这有一个毫无意义的bool变量“change”。这是个动词(用来描述动作),但这里的bool值是来描述一个状态,所以,这里应该用一个形容词更合适。

不好的代码:

1
2
3
4
5
6
const int maxcount = 1
bool change = true
public interface Repository
private string NAME
public class personaddress
void getallorders()

一段代码,只看它的一部分,你就应该直接明白它是什么类型,只需要看它的命名方法。

例如:你看到了“_name”,你就能知道它是个私有变量。你应该在任何地方都利用这种表示方法,没有例外情况。

清楚的代码:

1
2
3
4
5
6
const int MAXCOUNT = 1
bool isChanged = true
public interface IRepository
private string _name
public class PersonAddress
void GetAllOrders()

命名时相同的概念用相同的词表达

定义概念很难。在软件开发过程中,很多时间都花在分析业务场景、思考正确的定义里面所有的元素。这些概念永远都是让程序员头痛的事。

不好的代码:

1
2
3
4
5
6
7
//1.
void LoadSingleData()
void FetchDataFiltered()
Void GetAllData()
//2.
void SetDataToView();
void SetObjectValue( int value)

首先:

代码的作者试图表达“get the data”的概念,他使用了多个词“load”,“getch”,“get”。一个概念只用一个词表达就行了(在同一个场景中)。

第二:

“set”这个词用在了2个概念里:第一是“data loading to view”,第二个是“setting a value of object”。这是两个不同的概念,你应该使用不同的词。

清楚的代码:

1
2
3
4
5
6
7
//1.
void GetSingleData()
void GetDataFiltered()
Void GetAllData()
//2.
void LoadDataToView();
void SetObjectValue( int value)

命名时使用跟业务领域相关的词

程序员写的所有代码都是跟业务领域场景逻辑相连的。为了让所有关系到这个问题的人都能更好的理解,程序中应该使用在领域环境中有意义的名称。

不好的代码:

1
2
3
4
5
public class EntitiesRelation
{
Entity o1;
Entity o2;
}

当在编写针对某个领域的代码时,你应该始终考虑使用领域有联系的名称。在将来,当另外一个人(不仅是程序员,也许是测试人员)接触你的代码时,他能轻松的理解这个业务领域里你的代码是什么意思(不需要业务逻辑知识)。你首先考虑的应该是业务问题,之后才是如何解决。

清楚的代码:

1
2
3
4
5
public class ProductWithCategory
{
Entity product;
Entity category;
}

命名时使用在特定环境里有意义的词

代码里名称都有自己的上下文。上下文对于理解一个名称非常重要,因为它能提供额外的信息。让我们来看看一个典型的“地址”上下文:

不好的代码:

1
2
3
string addressCity;
string addressHomeNumber;
string addressPostCode;

在大多数情况中,“Post Code”通常是地址的一部分,很显然,邮政编码不能单独使用(除非你是在开发一个专门处理邮编的应用)。所以,没有必要在“PostCode”的前面加上“address”。更重要的,所以的这些信息都有一个上下文容环境,一个命名空间,一个类。

在面向对象编程中,这里应该用一个“Address”类来表达这个地址信息。

清楚的代码:

1
2
3
4
5
6
class Address
{
string city;
string homeNumber;
string postCode;
}

命名方法总结

概述起来,做为一个程序员,你应该:

  • 命名是来表达概念的
  • 注意名称长度,名称里只该含有必要的词语
  • 编码规范有助于理解代码,你应该使用它
  • 名称不要混用
  • 名称在业务领域里要有意义,在上下文里有意义
————————————————————————————————————————————————————

1、在每个代码范围内使用足够短和足够长的名称:例如循环计算器用一个字符就可以了,如i;条件和循环变量用一个单词,方法名1-2个单词,类名2-3个单词,全局变量3-4个单词组成

2、为变量指定一些专门名称,不要使用例如 “value”, “equals”, “data” 这样的变量名

3、变量名要使用有意义的名称,通过变量名能大概反映出其具体的用途

4、不要在变量名前加前缀,例如 o_, obj_, m_ 之类

5、服从公司命名规范,在命名时使用一致的做法,例如:txtUserName、lblUserName、cmbSchoolType, … 除非是大大降低了可读性

6、服从编程语言本身的规范,不要使用不连贯的小写和大写混合名称,例如:userName、 UserName、 USER_NAME、m_userName、 username, …

▲对 Java 而言:

▲类名使用驼峰命名方式,如:VelocityResponseWriter

▲包名小写:com.jobbole.project.ui

▲变量名小写开始的驼峰命名,例如:studentParentName

▲常量名用全大写:MAX_PARAMETER_COUNT = 100

▲枚举类名参考普通类名,枚举变量使用全大写

▲不使用任何带下划线的命名方式,除非是常量或者是枚举值

7、不要在同一个类的不同上下文中重用相同的变量名

8、不要在一个方法内用同一个变量做不同的用途

9、不要使用非 ASCII 字符的变量,例如中文变量

10、不要使用太长的变量名,例如50个字符,这很难阅读,而且可能超出一些编译器的限制

11、确定并坚持使用固定的一种自然语言的命名方式,例如不要使用拼音和英文混合的命名方式

12、方法名要使用有意义的名称,能通过方法名大概了解该方法的用途,而且方法名要以动词开始,例如createPasswordHash

13、方法名要符合公司统一的规范,例如:getTxtUserName(), getLblUserName(), isStudentApproved()

14、方法名要遵循编程语言规范

15、方法的参数名要使用有意义的名称

————————————————————————————————————————

一个软件最后都会落实到代码。而代码,其背后的架构设计或设计思想或模式固然重要,但我觉得更重要的东西则是良好的命名。混乱或错误的命名不仅让我们对代码难以理解,更糟糕的是,会误导我们的思维,导致对代码的理解完全错误。相反,良好的命名,则可以让我们的代码非常容易读懂,也能向读者正确表达事物以及逻辑的本质,从而使得代码的可维护性就大大增强,读命名好的文章是非常流畅的,会有一种享受的感觉。

另外一点也许大家还没感受到,那就是良好的命名,以及良好的命名习惯,由于我们总是对每个概念的名称要求非常苛刻,我们会思考这个名称所表达的概念是否正确,该名称是否正确表达了事物的本质或正确反映了某个行为的逻辑。所以,这种对命名的良好思考习惯,可以反过来帮助我们纠正之前的一些错误设计和代码实现;比如,你之前有一个地方可能命名不太准确,然后你发现后面有另一个地方需要用这个名字,且更合理。所以你会发现这个名字对前面的地方就不适合了,从而你会去思考前面的地方可能需要用其他的名字,或者你会发现前面的地方的设计根本就是有问题的。这种就是名字可以促使你思考你的设计是否正确的例子。

代码命名混乱或错误的主要原因:

  1. 没理解事物的本质;
  2. 理解了事物的本质,但不知道命名的重要性或者根本不屑于做好命名;
  3. 理解了事物的本质,也知道命名的重要性,但没能力命名好事物;

养成良好的命名习惯的一些想法:

  1. 对自己的严格自律,自己写代码时要有一种希望把每个名称都命名好的强烈意识和严格的自律意识;
  2. 要努力分析和思考当前被你命名的事物或逻辑的本质;这点非常关键,思考不深入,就会导致最后对这个事物的命名错误,因为你还没想清楚被你命名的事物是个什么东西;
  3. 在有自律意识和一定的分析能力基础之上,注意命名的方法技巧;要知道何时用动词,何时用名词;以及形容词放哪里,动词放哪里,名词放哪里;也就是小学时的主谓宾要会用;
  4. 你的任何一个属性的名字都要和其实际所代表的含义一致;你的任何一个方法所做的事情都要和该方法的名字的含义一致;
  5. 从代码的命名可以看出写代码的人编程时思路是否清晰,如果你对一个名字的命名不准确,很可能体现出你还没有理解这个名字背后的东西;
  6. 要让你的程序的每个相似的地方的命名风格总是一致的。不要一会儿大写,一会儿小写;一会儿全称一会儿简写;一会儿Pascal命名法,一会儿camel命名法或匈牙利命名法;
  7. 不要出现重复的命名;因为通常名称都有嵌套关系,比如类在命名空间里,方法在类里,所有如果一个概念在命名空间里表达了,那就不必再类上再表达一次;
  8. 对于属性或类名,应该总是名词在最后面,名词决定了这个属性代表什么,前面的部分都是用于修饰这个名词;比如,假如现在你有一个服务,然后又是一个关于订单的服务,那就可以命名为OrderService,这样命名就是告诉我们这是一个服务,然后是一个订单服务;再比如CancelOrderCommand,看到这个我们就知道这是一个Command,即命令,然后是什么命令呢?就是一个取消订单的命令,CancelOrder表示取消订单;
  9. 对于方法,应该总是动词开头,名词结尾;比如Order.AddItem(orderItem);这个,表示订单类有一个添加订单项的方法,Add是动词,表示添加,Item是名词表示订单项;
  10. 在C#中,我们一般用camel以及Pascal命名法,而不是匈牙利命名法。我觉得主要是两个原因:1)VS强大的智能感知提示的存在,我们没有必要突出变量的类型了,但这个我觉得只是一个次要原因;2)真正的原因,我上面有提到,一个变量,名词是放在最后的,这个名词决定了这个变量代表什么。比如有个变量叫totalCount,我们一看就知道这是一个count,然后count一定是一个int或者long,所以就不需要在强调它的类型了。再比如,remotingRequest, httpRequest,这种,我们也一看就知道他们是请求,一个是remoting的请求,一个是http的请求。remoting,http是用来修饰request的。request决定了这个变量是什么(同时就意味着我们知道了他的类型了),然后remoting,http这种是进一步说明该request的业务含义或当前上下文。就像disabledButton,我们一看就知道这是一个button,然后是一个什么Button呢,就是一个已禁用的button。所以,好的名称,本身就会让我们很容易知道该名称是什么东西,它的类型是什么,具有什么业务含义,所以没有必要再加类型缩写作为前缀;
  11. 多学习英文,多看国外优秀开源项目中的命名技巧,会对我们命名有很大帮助;

通过一些不太好的代码命名来分析一些简单的命名问题

以上代码中,有很多问题,我们来一一分析:

  1. 方法的参数,第一个字母,一会儿大写的P,一会儿小写的p,不一致;
  2. 第二个参数后面出现多余的空格,不应该;
  3. _paramsTable这个参数为什么要出现下划线,而其他参数没有下划线,不一致;
  4. publishRequest属于camel命名法,而iSignCounter, sStageIsOK这种属于另一种命名法,这种命名c++中用的多,不一致;
  5. foreach循环中,参数名叫instParam,但是后面的集合叫arrParams4SignActions,更对称一点的,应该叫arrInstParam;
  6. 方法的最后两行,出现多余的空格,导致代码格式排版混乱;

从上面的代码我们可以知道,仅仅是通过这些细节,就能发现很多问题。我们写代码时,只要多细心点,多注意点排版是否美观一致、命名是否统一,那代码写出来就会漂亮很多了。下面我们再看看其他的代码:

  1. 上面的代码中,两个参数的命名也不一致,projectid中,i是小写,但是publishId参数,i却是大写,应该都统一为大写;
  2. ViewData中的key,一会儿是全部大写的UPDATE,一会儿是另一种命名,不一致;
  3. 上面的两个红框标出来的if,虽然都是只有一行代码,但是一个有括号,一个没有括号,不一致;且第二个if里出现了多余的空行,格式混乱;

  1. 上面的代码中,函数中,一会儿用IList,是一个接口,一会儿用Dictionary,非接口,不一致;应该都用接口,或者都不用接口;
  2. listOriginal和receiverList命名不一致,要么全部list开头,要么全部List结尾;
  3. foreach循环中,变量的类型叫TDMSOriginalRequirement,但是变量名却叫originalItem,而集合名称又叫listOriginal,应该三者统一;比如foreach (Assembly assembly in assemblies)
  4. +“...”这个地方没有用空格,加号两边应该要空格,这属于格式混乱,不严谨;
  5. createUser这个变量取的很不理想,create是动词,createUser合起来就是创建用户的意思,而他这里要表达的意思是创建人的意思,所以应该叫createdUser或者creator;
  6. 为何originalItemFormat和originalItem的意思可以等价,不合理,如果等价,一开始就要命名为originalItemFormat;而且format是一个东西,动词放在最后,算个啥?

  1. 上面这个类的几个私有字段中,有些带命名空间,有些不带,要么都不带,要么都带;一般命名空间都是在上面声明,后续不需要出现;
  2. ILog logger;这一句有两个问题:1)logger为何没有下划线,不统一;2)为何类名叫ILog,而变量名叫logger,要统一,要么类名叫ILogger,要么变量名叫_log;

上面这两个私有方法,一个是大写开头,一个是小写开头,不一致,混乱;应该要一致;

总结

通过上面的一些例子,我们知道,在我们不经意间,多写了一个空格或者一个空行,或者一个字母的大小写不一致了,都会导致命名的不一致;如果自己没有养成这种平时注重代码命名各种一致性的习惯,那写出来的代码很可能就是像上面那样。我觉得是非常糟糕的。上面我举的例子都只是简单的命名方面的,更深层次的命名问题,比如如何做到名称和其背后的实现内容一致,这个是需要我们平时不断修炼的。不是短时间内就可以做到那个程度。

我觉得,要做好命名,归根结底:

  1. 先要意识到命名的重要性;
  2. 要端正态度,要认真的写代码;
  3. 要努力推敲每个名称和其实际做的事情是否一致,也就是命名的准确性;
  4. 要时刻注意命名的各种一致性;

养成良好的命名习惯不是为了别人,不是为了公司,而是为了提高自己的编程修养,提高自己认识事物的能力。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值