denleyhsiao
7/3/2017 - 3:17 AM

Package manage.

Package manage.

语言包管理工具相关文档与资源
PerlCPAN[1] [2]
PHPPEAR PECL[1] [2]
RubyRubyGems Bundler[1] [2]
JavaMaven[1] [2]
PythonEasyInstall PyPI[1] [2]
NETNuGet[1]
NodeJSnpm[1]

之前在管理依赖的时候尝试过,大概说说第一印象:自身安装包:全平台覆盖,有完善的binary这很方便。但是基于Python可能在一些场合会带来麻烦,更倾向于搞出一个bulkcompile的.cpp,这样可以无痛切换到conan而不会因为自身而带来额外的依赖,毕竟不是所有项目都希望引入Py。是采用Binary还是Source是每个包管理都会遇到的问题,binary需要维护,source对于Cpp来说编译速度太慢。目前Conan貌似是对大包提供了Binary,找不到时会fallback到Source上去,看起来是一个比较好的解决方案。但是不知道二进制兼容性(RTTI、cxxabi)这类的做得怎么样(更新:看C/C++ Open Source Package Manager ,大概对每个编译器大版本都提供了Release和Debug两个二进制版本,这样解决了abi问题,但RTTI估计是没戏了)。不过好在可以自己架服务器提供包。目前的官方包还是太少,像leveldb之类的都找不到CMake集成做的不错依赖管理其实本身就是一个矛盾体,如下表

目录结构

好的开源项目,通常会选择合理的目录结构,来组织自己的代码。而所谓合理,通常意味着遵循最常见的约定俗成。比如:

目录名含义
conf/configure各种配置文件
src/source项目的源代码
doc/document项目文档
test/unittest单元测试
tools/utils相关工具
lib库文件
app应用相关的文件(在web项目中经常出现)
controllers控制器,在遵循MVC模式的Web项目中,经常出现
models模型,在遵循MVC模式的Web项目中,经常出现
views视图,在遵循MVC模式的Web项目中,经常出现
db数据库相关文件
demo/example相关示例代码
misc其他杂项
include头文件所在目录,c/c++项目中常见
out/build编译结果输出目录
third_party/vender第三方库
install安装所需的相关文件

包名与文件名

在软件体系中,包(Package)是一个很重要的概念,与模块(Module)类似,但是又有所区别。一个项目,在初始设计时,就需要做模块划分,每一个大小合适的模块,往往就可以作为一个开发工作单元,分配给某个开发者完成。当然,对于那种大型的、复杂的项目,还需对模块做进一步的细分,比如:子模块(Sub Module)。而包(Package),则往往具有一定的可重用性。我们可以认为,一个模块,开源出去未必会有人来用。而一个设计良好的包,本身就可以作为一个开源项目,放出去给被人使用。

因此,从更加有利于软件开发的协作的角度来说,合理的包命名,就变得非常重要。

越是现代的开源项目,越是懂得不必一切从零开始搭建,所以,我们常常会发现,一个开源项目,他们自己会开发一组Package,同时也依赖一批别人开发的Package。在静态理解项目时,了解一个项目项目有哪些包,以及依赖哪些包,就非常重要。

举例之一:rails是一个著名的ruby开源项目。我们访问它的github主页,就可以看到这个项目的源代码结构。

不同的项目,描述包文件,以及包依赖关系,有各种不同的格式。需要一一分别学习。这里就不再详细解说了。

在一个开源项目中,代码当然是由一个一个的源代码文件组成的。通过查看文件名,往往可以了解一个文件的大概内容。例如:

  • errors.rb,通常会与出错处理有关
  • i18n.rb,通常会与国际化有关
  • logger.rb,通常会与日志有关
  • json目录下的两个文件decoding.rb和encoding.rb,自然是JSON格式的编解码相关代码

通常,要迅速的辨认出一个文件名的含义,与领域知识大有关系。例如:http.rb,通常会是处理http协议相关。而request和response,则通常是网络协议中的请求与响应相关的处理代码。对于这些单词的熟悉程度,决定了我们阅读与理解代码的迅捷程度。

类名、函数名与变量名

java是一门很讲究规范的语言,所以他的每一个类,就会对应一个同名的.java文件(内部类除外)。这使得我们寻找类所在的源文件,变得非常简单。当然,这样会造成源文件数量的增加,也许会有人不喜欢。

不同的语言,对于命名有其自己的规范,我们可以做一个列表,来简单列出这些规范。

语言包/命名空间命名类命名函数/方法命名常量命名变量命名
Javadomainname.package 全部都是小写的单词,以.区隔ThisClassName 每个单词都以大写字母开头theMethodName 第一个单词以小写字母开头THE_VALUE 全部大写,单词以下划线分隔theValue 与函数名一致
C#DomainName.Package 每个单词都以大写字母开头,以.区隔ThisClassName 每个单词都以大写字母开头TheMethondName 与类名一致THE_VALUE 全部大写,单词以下划线分隔TheValue 与类名一致
PHPdomainname.package 全部都是小写的单词,以.区隔ThisClassName 每个单词都以大写字母开头theMethodName 第一个单词以小写字母开头$THE_VALUE 全部大写,单词以下划线分隔$theValue 与函数名一致
C/C++std::hex 全部小写,以::区隔CThisClassName 以大写C开头,后续是大写字母开头的单词TheMethodName 每个单词以大写字母开头nMAX_VALUE 特别会引入前缀的概念,例如:n代表整形、b代表布尔型、c代表字符型等等nTheValue 与常量类似,单词区分大小写
DelphiMyUnit.Unit2 遵循Pascal命名法:一个名字里如果包含多个单词,每个单词的首字母都要大写,以.区隔TThisClassName 以大写T开头,后续是大写字母开头的单词TheMethodName 每个单词以大写字母开头castMaxValue 特别会引入前缀的概念,例如:i代表整形、b代表布尔型、c代表字符型等等,cast代表常量iTheValue 与常量类似,单词区分大小写
RubyModule::SubModule 每个单词以大写开头,以::区隔ThisClassName 每个单词都以大写字母开头the_method_name 全小写单词,以下划线分隔,!?有特定的含义MAX_VALUE 全大写单词,以下划线分隔the_value 全小写单词,以下划线分隔
Pythonmod_submod 全部小写,以_区隔ThisClassName 每个单词都以大写字母开头the_method_name/theMethodName 全小写单词,以下划线分隔,也可以类似Java的命名,私有函数以双下划线开头MAX_VALUE 全大写,以下划线分隔the_value 全小写,以下划线分隔
JavaScriptThisClassName 每个单词都以大写字母开头thePrivateMethod/ThePublicMethod 私有函数小写字母开头,公有函数以大写字母开头MAX_VALUE 全大写,下划线分隔theValue 小写字母开头,私有变量,加下划线

这是一个非常粗略,挂一漏万的表格,详细的命名规范,请参考各种具体语言的命名规范文档。

注释与Readme

很多时候,开源项目代码里的注释,会给你带来误导。或者会让人不知所云,或者只是写给自己看到TODO,或者代码改了,注释忘记改。种种原因,因此我强烈的不建议过于重视注释。

但是,在业内有一种流派,非常重视注释,而且信奉从源代码的注释,就可以直接生成项目的开发文档。比如JavaDoc这样的东西,在java的开源项目里,简直用到泛滥,也造成了java的很多项目,注释数量比代码的数量还要多。而且,格式规范,千篇一律(为了生成Document),真正有意义的注释内容,少之又少。纯粹是干扰阅读。

很多时候,我都建议阅读代码,就真的去读代码。然后试着从类名、方法名、变量名中,大致“猜出”代码的意义。然后再实际的将代码运行起来,看看执行过程中,这些代码是如何工作的。

总之,不到万不得已,不要先看注释。

虽然我个人对于注释,相当的不重视,但是却非常认同Readme的价值。令我倍感欣慰的是,Github的创立者,也完全赞成这一点。他们将一个项目的首页,直接规定为“代码展示+Readme”,也就强迫所有在Github上安家的开源项目,将更多的精力,投注到Readme的撰写中去。这样形成的良性循环,使得我们可以乐观的预期:越来越多的开源软件,将会越来越重视项目根目录下的Readme文件的价值,将一个项目最为重要的内容,以最为精炼的方式,在Readme中,以结构良好的方式,展现出来。

因此,首先阅读Readme,对于了解一个开源项目,是一个非常好的选择。