开源代码认知
最近被同事问到开源的代码标准是什么?一时脑海懵然,自己在GitHub上也开源很多代码,最初的想法简单,保存自己的代码(这个代码没有质量可言),方便自己日后查找,随着能力提升,发现之前上传到GitHub上的代码没什么鸟用,后来就上传日常开发中的技术难点或者新技术研发记录、项目中需要用到控件封装等,希望能帮助到遇到相同困难的人,能提供一种思路。
开源的概念
开放原始码由Bruce Perens(Debian创始人之一)定义。
- 开放源代码(Open source code)也称为源代码公开,指的是一种软件发布模式。
- 一般的软件仅可取得已经过编译的二进制可执行档,通常只有软件的作者或著作权所有者等拥有程序的原始码。
- 有些软件的作者会将原始码公开,此称之为“源代码公开”,但这并不一定符合“开放源代码”的定义及条件,因为作者可能会设定公开原始码的条件限制,例如限制可阅读原始码的对象、限制衍生品等。
- 源程序(source code)是指未编译的按照一定的程序设计语言规范书写的文本文件。 源代码(也称源程序),是指一系列人类可读的计算机语言指令。 在现代程序语言中,源代码可以是以书籍或者磁带的形式出现,但最为常用的格式是文本文件,这种典型格式的目的是为了编译出计算机程序。计算机源代码的最终目的是将人类可读的文本翻译成为计算机可以执行的二进制指令,这种过程叫做编译,通过编译器完成。
开源的意义
“存在即合理”是一个自然规律。开源这样形式组织能够持续的存在,有其内在的价值,也有其源源不断的生命力。
简单说⼀下当前对开源代码意义描述较多的⼀些观点:
- 实现软件“共产主义”,借鉴开源代码,仅编写与⾃⼰应⽤紧密相关的代码,即可达成⽬标;或者是集成当前多种开源代码组件,仅做仅少量的控制参数,也可达成⽬标。
- 打击⼤⼚垄断(譬如:微软、英特尔等),降低软件产品价格,给消费者带来了显⽽易见的利益。
我对开源代码的理解:
- 于公,为社会快速发展发展贡献⾃⼰的⼀份⼒量,展现企业的⼀份责任⼼。
- 于私,进⼀步提升企业品牌价值;提升企业在部分科技⼈才⼼⽬中的地位。
- 引领技术发展,提高行业整体水平。无国界的交流、学习平台
开源的标准
业界还真没有一个评判标准,在开发人员每个阶段的代码的认知都是不一样的,跟工作经历和工作经验息息相关,每个人都有自己主观的评判标准,有可能昨天你觉得很优秀的代码今天依然看着很low;一个工作经验丰富的人看新人写的代码,心情也是各种复杂。
最近一段时间总结一些代码评判标准的一些看法,当然这也只是我自己的理解,能够更靠近标准。可以用一个公式表达:
标准代码=场景+基础知识+设计原则+设计模式+注释+单元测试+工作经验;
别看这只是一个简单的公式,消化起来也得花上几年的时间。
项目中代码的痛点:
现实有很多这样的情况,项目的周期很长,维护项目的人一波又一波,到最后都不知道这些代码什么这么写,出现问题了也只关注局部,解决当前问题;很少考虑到源头什么去这么做,这个问题有可能引起哪些问题,如果需要重构的话有什么优化手段。
场景化
所有的代码不可能凭空产生,首先我们要清楚前提是一个什么场景,这个场景有哪些需求,每个需求有哪些功能点;代码开始做设计的时候需要知道具体的场景,代码重构的时候也需要知道具体的场景;现实有很多这样的情况,项目的周期很长,维护项目的人一波又一波,到最后都不知道这些代码什么这么写,出现问题了也只关注局部,解决当前问题;很少考虑到源头什么去这么做,这个问题有可能引起哪些问题,如果需要重构的话有什么优化手段。
说到底就是怎么维护场景和代码的关系,通过场景知道代码实现,根据代码实现找到具体的场景;详细设计文档的作用就体现出来了;详细设计文档主要记录场景说明,前置步骤、触发条件,时序图、类图、接口输入输出;那这样又会带来一个问题,谁去维护详细设计文档维护文档是一个比较索而无味的事情,一些程序员宁愿写代码也不愿意写文档,所以还是需要有负责人去维护。
基础知识
代码基础知识是一个程序员的基石,不用多说,大家都懂;但是还是需要时常去回顾一下,以前学习的时候是学怎么用?而现在我们需要考虑的是为什么需要这样用!能给我们带来什么好处,用这些基础知识能解决那些问题。一般场景的就是封装、集成、抽象、多态、面向对象、面向接口的知识。
设计原则
设计原则可以说是比较实在的东西的,可以作为一种代码设计和评审的重要依据;一些叫有规模的项目在设计的时候都会去遵循这一套设计原则,那我们常说的设计原则有哪些呢?SOLID原则
SRP: Single Responsibility Principle,一个类或者模块只负责完成一个职责;
OCP:Open Closed Principle,软件实体(模块、类、方法等)应该“对扩展开发,对修改关闭”;
LSP:Liskov Substitution Principle,子对象能够替换程序中父类对象出现的任何地方,并且保证原来的程序逻辑行为不变及正确性不被破坏;
ISP: Interface Segregation Principle,客户端应该不强迫依赖它不需要的接口;
DIP:Dependency Inversion Principle,高层模块不要依赖底层模块,高层模块和底层模块之间应该通过抽象来相互依赖,除此之外,抽象不要依赖具体的实现细节,具体实现细节依赖抽象。
KISS原则:KISS=Keep It Short and Simple.(尽量保持简单。)
YAGNI原则:You Ain’t Gonna Need It.(不要做过度设计。)
DRY原则:Don’t Repeat Yourself.(不要写重复的代码。)
设计模式
设计模式的产生是基于设计原则的,如果说设计原则是代码标准,那么设计模式就是基于设计原则的工具类,也是对设计原则的最佳实践。我们常听的23种设计模式也是分别解决不同场景下的代码论;那是不是就只有23种设计模式呢?显示不是的,只能说这23种设计模式覆盖大部分场景,自己也可以创建一种设计模式。随着时间的推移23种设计模式已经成为了标杆,23种设计模式主要是哪些?
创建型:代理模式、工厂模式、建造者模式、原型模式;
结构型:代理模式、适配器模式、桥接模式、门面模式、装饰器模式、组合模式、享元模式
行为型:观察者模式、迭代器模式、命令模式、模板模式、状态模式、解释器模式、策略模式、访问者模式、中介模式、责任链模式、备忘录模式;
注释
注释是项目开发过程中非常重要的一个环节,我经历了很多项目,真正能把注释落地而且规范的是比较少的,在我个人看来,我们开发的项目和开源的项目来开距离的就是注释。我们开发的项目的注解是怎么样的呢?类注释会简单的描述类的作用,基本一句话总结,也会加上日期、作者之类的;方法也是类似;而且很多时候是开发的时候不会去写注释,在开发完成之后在去补充对应的注释,对于小白来说,很难通过这些注解知道一些细节性的东西,只能通过代码查看。对于开源项目是怎么做的呢?我们以Spring框架来举例,第一点类上面的注释非常详细,主要包含这个类是做什么的、有哪些配置、这个类关联了哪些类,这个类有哪些主要方法,最重要的是还有示例代码,通过html标签的形式展示,也方便javadoc查阅,当我们查看注释文档的时候,而不看代码我们就知道这个类的大致设计。这方面的差距还是需要多看源码来弥补的。
单元测试
单元测试是保证代码质量的的一种实现方式,但是大家都不愿意写单元测试,比较麻烦还比较耗时;之前经历过一些项目,那都是迭代周期比较短,几天时间从设计开发、测试、上线,基本上是没有多余的事情去做单测;还有一种情况就是行为方式,习惯了手动测试的方式,以最终运行的结果为准。这样会带来一些弊端,第一:场景覆盖不全,手工测试,需要创造各种场景,而且有些情况不一定能够模拟出来,测试的周期将会更长,到测试问题有转开发,实际上周期会拉长;第二点:当新增或者是修改功能的时候,需要将之前的功能从新手动跑一次,也会加长整个开发周期。建议呢,在项目中必须要养成写单元测试的习惯,不仅能够检验逻辑的正确性,也能够减少整个开发周期带来的额外的时间。
工作经验
工作经历越长经历的各种场景也就越多,深受不规范带来的坑的痛苦,他们会刻意的避免一些坑,注重一些代码规模,像阿里的开发手册规范,就是积累了很多前辈的心血,也是很多前辈踩过的坑。工作经历长的人会注重代码优化和并发问题,举个例子:在Spring的bean组件定义全局变量,然后再方法里面去修改值,有经验的人一样就会看出来会有并发问题,因为bean组件是单例,并发场景下多有多个线程去修改,导致线程不安全。工作年限久的不一定比刚出来的人掌握的知识多,但他遇过的坑肯定是你多的,所有积累项目经验,想前辈学习也是重要的过程。
总结语
每个人在每个阶段的对代码的理解程度是不一样的,同一样的代码在不同的时间感悟也是不一样的,在真正理解代码的程度上;代码里开源标准也就不远了;所以说项目要达到开源指标,还是有一些难度,也就是需要被大多数使用者理解的代码才算是真正的开源标准。
开源代码评审
谷歌以前建立了一套通用的工程实战指南,它差不多囊括了所有编程语言与各种类型的项目。今天,谷歌将这一套代码评审(Code Review)规范开源了出来,它代表了谷歌最佳实战经验的集合。
项目地址:https://github.com/google/eng-practice
开源项目作者或其它开发者都能从这个项目获得有用的知识,因此谷歌开源了这一份代码规范,并将持续维护。如项目所言,目前这份代码评审规范主要包含两组独立的文档:
1. 代码评审者的指南
-
代码评审标准
-
代码评审希望达到什么
-
在代码评审中导航修改列表
-
代码评审的速度
-
如何写审查的评论
-
处理代码评审的回退
2.CL 作者指南
-
写一个好的修改列表描述
-
构建一些小的修改列表
-
如何处理代码评审者的评论
其中代码评审者指南包括一些做代码评审的最佳方式,它们都是根据长期经验得出来的。代码评审者指南本来是一个完整的文档,但作者将其分为了 6 部分,读者可根据需要阅读。修改列表(Change List/CL)制定者指南包括一些浏览代码评审的最佳方式,开发者可以快速处理评审结果。
代码评审最主要的目的是确保代码库一直保持「健康」的状态,代码评审的所有工具和过程都是为了这个目的而构建的。代码评审会系统化地查一遍源代码,并希望检查出开发初期未察觉的一些错误,从而提升代码质量。
那么代码评审都在干什么呢?一般而言,代码评审希望完成以下的评估:
-
设计:代码是不是经过精心的设计,并适合我们的系统?
-
功能性:代码的行为是否和作者的意图保持一致?代码的行为方式对用户是否正常?
-
复杂度:代码能更简单一些吗?在未来,其它开发者能更容易地理解并使用这些代码吗?
-
测试:代码是不是正确的,是不是通过了精心设计的自动测试?
-
命名:开发者是不是选择易于理解的名称给变量、类和方法进行命名?
-
评论:代码评论是不是足够清晰并有用?
-
风格:代码是不是采用了标准的编写风格?
-
文档:开发者是不是更新了相关的文档?
既然代码评审要进行众多的检查,那么找一个优秀的评审者就非常重要了。一般对于修改列表的不同部分,都会有不同的评审者进行细致的审查。另外,关注公众号Java技术栈回复手册可以获取阿里巴巴的最新Java开发手册,非常有价值和参考意义。
当然如果是结对编程,且你的队友能进行高质量的代码评审,那么这样写的代码一般可以视为已经过评审了。此外,我们也可以进行面对面的评审,评审者会问开发者一些问题。
谷歌表示他们以如下规则作为期望的标准:
「通常而言,一旦修改列表能提升整体代码的健康程度,那么即使修改列表不完善,评审者同样也应该倾向于批准该列表。」
这条准则是所有代码评审指南的最高原则。它也会有一些限制,例如,如果 CL 添加了一些评审者不需要的特性,那么即使代码经过了精心的设计,评审者也应该不予通过。
这里的一个关键点是没有「完美」代码这个概念,只有更好的代码。评审者不应该要求代码作者在批准前对每一小块 CL 进行打磨。
相反,评审者应该权衡向前继续开发的需求和修改建议的重要性。评审者要求的是持续性地改进,而不是追求完美的代码。CL 作为一个整体,如果它能提升系统的可维护性、可读性和可理解性,那么就不要因为它还不完美而推迟数天或数周更新。
评审者应该经常留下一些评论,以表达能导致更好性能的做法。如果这些做法并不是非常重要的,那么需要加上前缀「Nit:」,从而令代码作者知道这些内容是可以忽略的。
评审指导
代码评审有一个很重要的功能,即教开发者一些开发经验,不论是语言、框架还是一般软件设计准则。留一些评论总会帮助开发者学习一些新的知识,共享知识也是改善系统代码健康状态的重要部分。当然,如果评审者的评论仅仅只是教育性的,且对于标准要求不那么重要,那么还是要加上前缀「Nit:」的。
评审准则
技术事实和数据要优先于观点与个人风格。
在代码风格方面,谷歌的代码风格指南是最权威的参考资料。任何不在风格指南中的代码习惯,都属于个人风格,但我们应该保证基本的风格和谷歌风格指南是一致的。
谷歌风格指南:http://google.github.io/styleguide/
软件设计方面几乎不会有纯粹的风格问题,或者纯粹个人的习惯问题。很多风格问题都基于一些基本准测,它们并不是简单地由个人观点决定的。此外,如果代码作者通过数据或基本工程原则证明了几种方法同样有效,那么评审者应该接受作者的风格。否则,偏好的选择还是取决于软件设计的标准原则。
如果没有其它适用规则,那么评审者可以要求作者的偏好与当前代码库保持一致,同时不对整体的代码健康水平产生影响。
解决冲突
在代码评审中,如果发生了任何冲突,第一步应该是开发者和评审者基于本项目的 CL 指南达成共识。当达成共识非常困难时,开发者与评审者应该面对面地交流,而不只是通过审查中的评论来交流。如果开会讨论还解决不了,那么就要扩大会议了,我们可以通过与代码维护人员、工程经理等开发者的交流,达成最终的共识。
参考:
1、开发源代码百度百科
2、开源的意义知乎
3、如何让写的代码达到开源标准CSDN
4、谷歌开源代码评审规范