首页 > 代码库 > 腐败的程序员(1)

腐败的程序员(1)

如果你不幸的看到了此文章,请不要急着喷lz,且当楼主是个标题党吧。

 

一直以来,腐败这个词都是放在那些掌握有一些权利的人身上的,程序员怎么会腐败呢?我天朝程序员是非常敬业的,别人在家享受家庭的温暖的时候,我们可爱的程序员一族还常常在公司里无怨无悔的加班。其实lz想说的主题是怎么样写出高质量的代码,但是高质量的代码这个主题太高深,楼主不敢用,所以用了这么一个标题。下面进入正题。

 

作为lz的第一篇博客,先立出一个观点,高质量的代码是在严格的约束中产生的,但是严格的约束却不一定能产生高质量的代码。为什么高质量的代码要产生于严格的约束,那是因为程序员都“太懒”。今天说到的第一个约束是关于面向对象编程中最常用的三个关键字,public,protect,private。但凡是用过C++,java,C#等所谓的具有面向对象编程范

式的语言的人对这三个字一定不陌生,他们是用来约束类成员的可见范围的,public基本上没有约束,protect一般把成员约束在类本身和派生类中可见,private最为严格,只有在本类中才可见。首先说说怎么来理解这三个修饰词:

(1)这种可见约束一般是编译时约束,就是说这是编译器在编译的时候会去检查代码中每个变量在当前上下文是否可见。如果你使用过C或者汇编语言,这些语言一般没有这些关键字,当然C语言有static,可以把函数的可见范围约束在本文件内。因为在计算机执行指令的时候,看到的只有地址,指令,计算机只要能找到你的地址,那他就可以读取地址对应的数据,并且执行这些指令(这是程序安全漏洞的根源)。由此看来这三个关键字其实是在自己束缚自己的手脚,为什么要“作茧自缚”呢,有这个必要吗?可以肯定的是有这个必要。

 

(2)在那些纯面向对象编程语言的世界里面有句话叫做一切皆对象,那类又是什么呢?你是怎么理解这句话的,一切真的皆对象吗,甚至从编码的角度来说,应该是一切皆类。假如你用C#编码,你回过头去看看你的代码,其实你会发现你是在定义一个又一个的类,把所有的类都定义好了,编译,运行,OK完事了。所以类成员的使用有两个区域,类本身,别的类。那一些皆对象有错吗?回答是没有,因为这是在另外一个层面的含义。那类和对象的关系又是什么?客观接着往下看。

 

(3)类是什么,对象又是什么?本人给出的理解,类就是写在纸上的协议,对象是按照纸上的协议制造的实物。在一个抽象的世界里面,一切皆是类,在现实的运行层面一切皆是对象。类使用对象来描述自己。我靠,这有点像蛋生鸡,鸡生蛋的问题,那是先有鸡还是先有蛋呢?面向对象编程给出了明确的答案,先有鸡。怎么理解呢?面向对象的编程哲学暗示了先有上帝,然后创造了世界。谁是上帝?就是你程序的运行环境。对于java来说,上帝就是java虚拟机,对于C#来说就是.net runtime,对于C++来说就是操作系统。事情是这样的,上帝先构想了一个世界(抽象层面),这个世界是由基本的粒子(运行层面)组成,也就是物理界所定义的不可再分的粒子,然后这些粒子自由组合就构成了世界。所以我们程序的每次运行都是一个新世界的诞生。计算机的基本粒子是什么,很明显,就是一个bit。上帝发现用基本粒子来描述世界太麻烦,于是定义类100多种元素(抽象层面),计算机世界发现bit不好用,于是定义了基本数据类型(byte、int等等)。说到这里就可以说,基本粒子就是那只鸡,计算机世界的bit就是那只鸡。

 

(4)看了第(2)(3)你会说你这些废话和public,protect,private有关系吗?有。现实的运行是对象,对象需要和外界交换信息,public就是用来定义一个对象如何与其他对象交换信息的协议修饰词。如果对象不和外界交换信息,那就是一个孤立的世界,是没有意义的。private修饰的成员别的类是不能用的。那protect修饰的成员又如何理解,我们知道类定义了一个协议,可是还有一些协议是在原有的协议上扩展而成的协议,所以原有协议中protect修饰的成员在扩展协议中还可以继续使用。好了,以上是一些我的粗浅的理解,回到问题上来,我们应该怎么使用public,protect,private这三个关键词。 有了以上的理解,这三个词的用法就很明确:

 

(1)对外公开的接口就要定义为public,这里面分为数据成员和函数成员,这两种成员如何理解,数据成员就是你对外提供的信息,函数成员用于交换信息。在C#和java世界里面都不建议直接暴露数据成员,而是用set、get,setter、getter代替,这是最完美的方案吗?不见得,lz认为,get和getter的存在没有必要,完完全全是多余的,而set、setter的存在需要视情况而定,如果对外界提供的数据有限制,那就是应该使用set、setter,而且是必须的,如果没有限制,就没有必要使用set、setter,这完全是无意义的。我想java和C#可能是为了保持统一、和谐、对称的美而提出的这条建议吧。当然聪明的你会说,现在的语法没法做到,如果数据成员不为public,就没法直接读取,如果数据成员为public就无法阻止直接赋值,但是假如你作为语言设计者有没有办法解决这个问题,你自己想。      

  对于函数成员,用法就简单多了,如果需要公开接口,就设为public。否则就一定不能为public。但是大家都是这样使用的吗?不见得。这是我要批判一个问题,也是写这篇文章的目的之一,在我接手过的很多的项目里面,函数成员基本上全是public,为什么存在这种情况,值得深思。首先说说这样做最直接的坏处,就是没人敢修改你的代码,因为把函数成员定义为public,意味着接口是公开的,这将影响所有使用了此接口的代码,但是没人知道,使用这个接口的人和你当初设想提供这个接口的目的是相同的。一旦使用这个接口的人和你提供接口的目的不同,你如果去修改这个接口的实现代码,很有可能导致调用此接口的代码发生逻辑错误。所以public修饰的函数必须非常的慎重,一定要通过各种办法来促使使用此接口的人和你提供此接口的目的相同。     

  那为什么很多可爱的程序员会把不该公开的接口设为public呢?我想这里面的原因是多种多样的,但是其中一定有一个原因是相同的,那就是他在编码的时候不知道这个类应该对外提供什么接口,换句话说,他对这个类的职责(公开的接口定义了所有的职责)非常的不明确,他不知道哪些接口需要公开,哪些不需要。所以这里定义一个程序员腐败的特征之一:把不该公开的接口定义为public。我想高质量代码的特征之一至少要包括“不把不该公开的接口定义为public”这一条。这一条约束肯定会影响你编码的速度,因为他会促使你思考你这个类的真正职责,这是一种自我约束,没人管得了你,要不要腐败你自己决定。

 

(2)protect的使用就比较讲究,首先,如果你定义的这个类明确不能被继承,那你就应该不要使用它。我遇到的很多朋友是这样考虑问题的,一个类里面如果搞不清这个接口是否需要公开,那就定义成public,其他的一概定义为protect,你有这样做过吗?这种代码更加难以维护,可以这样说,一旦一个类里面定义了protect函数成员,那就间接说明了很有可能已经被继承了。这就会回到(1)提到的问题的噩梦中。一个类是否可以被继承是一个需要深度思考的问题。深度思考一个类是否可以被继承,以及被继承后类的演化是高质量代码的关键标志之一,这也是系统对修改关闭对扩展开放的主要手段之一。非常多的设计模式主要特征都是定义了类的继承,以及继承后的演化问题。

 

(3)private是最容易使用的,因为它带来的副作用最小。只要在你的类把成员函数定义为private以后,系统能正常运行,一般就万事大吉了。因为后续发生的事情和private的使用一般就没有关系了。但是如果一个本应该公开的接口或者可以被继承的接口定义成了private,那么就不能发挥此类的最大威力了。

 

小结:public,protect,private这三个关键字大家都经常使用,你可以非常考究的使用,也可以随意的乱用。相信我,仔细思考他们的使用场景有助你写出高质量的代码。

腐败的程序员(1)