Jade Dungeon

开发环境配置

需求分析

程序的开发工作是一项相当复杂的工作,仅仅是在开发工具的支持上就需要有代码编辑 、编译打包可执行文件、资源文件预处理、软件测试等许多功能: \cite{plain:javaAgen}

开发工具需求分析

为了支持图\ref{fig:ch04.tool.chain.png}中所列出的各项功能,开发人员可以有两种 选择:

  • 使用集成开发环境(IDE)
  • 针对每一项需求选择最合适的程序,通过配置把相互独立的程序整合起来,形成 自己的工具链(Tool Chain)。

用工具链代替集成开发环境的优势

虽然在如今的编程活动中集成开发环境(IDE)的使用已经非常普遍,但还是有非常多的 程序员更加习惯使用自己配置的工具链来代替IDE。接下来需要通过比较使用IDE与 自己构建工具链这两种工作方式的优缺点来决定在本项目是使用哪种方法更加合适。

与自己配置工具链相比,使用IDE的优点有:

  • 开箱即用:IDE一般都不需要过多地配置,安装完以后就可以直接使用。
  • 集成度高:自带大量的开发工具,可以方便地完成代码的编写、重构、程序构造、调试 等一系列的操作。

但是与自己配置工具链相比,IDE的缺点也很明显:

首先,各种IDE相互不通用。而且每一款IDE都只擅长处理个别几种语言。比如C#程序 最适合使用Visual Studio,而Java程序适合用Eclipse或是NetBean。对于一个同时 使用C#和Java两种编程语言的程序员来需要学习两款不同的IDE,而且两款IDE的快捷键、 菜单、操作方式都不一样。而在使用自建工具链的情况下可以选择程序员自己习惯的 工具,比如无论使用的是哪一种程序语言都可以使用Vim或Emacs来编程源代码,不用重新 学习另一种工具。

其次,IDE的定制性与扩展性也不如自己配置的工具链。虽然IDE也可以提供一定的定制性与 插件扩展,但毕竟范畴还是有限的。而使用自己配置的工具链时工作中的每一步都可以 选择开发人员自己最习惯的或是最全程解当前问题的程序,所以定制性与扩展性更高。 因为很多工具链中的程序都秉承了UNIX设计哲学的一贯风格:「第一个程序只设计用来 完成单一的一个任务,并且设法做到最好」(Write programs that do one thing and do it well)\cite{plain:unixPhil}。

最后,虽然自己配置工具链看起来不如IDE的开箱即用方便,其实工具链的配置是穿插在 开发人员学习一门新编程语言的过程中的,并不会占用太多的实际工作时间。而且在 学习一门新编程语言的过程中学着自己配置工具链的话也能进一步了解语言编译的过程 实际上还是非常有益的。

由于本项目主要由Scala语言来实现,而目前主流的IDE对于Scala语言的支持都不完善。 所以使用工具链代替集成开发环境更加合适。

文本编辑器vim

源代码的编写对于文本编辑器地要求比较高。不仅要功能强大,而且扩展性要强才能适应 在编程工作中遇到各种需求。在这个项目里所使用的文本编辑器是著名的vim,它不仅 有着强大的扩展性,并且还可以按照需求进行高度的定制化。

在没有任何配置的情况下,vim已经是一个功能非常强大的文本编辑器了。接下来的工作 是要把它配置成一个适合于Scala项目开发的工具。

语法高亮

已经有很多现在的工具可以实现在vim中对Scala代码进行语法高亮的显示。比如说 scala-dist。可以在 https://github.com/scala/scala-dist 下载。安装过程也 非常简单,只要解压后复制tool-support/src/vim到当前用户的.vim下即可。

标记跳转

在编写程序的过程中经常需要查看变量定义的位置、或是跳转到定义类与方法的源文件中 去查看源代码是如何定义的。还常常要补全各种类名、方法名、变量名来减轻程序员的 记忆负担。这样的功能在像是Eclipse这样的IDE中是直接自带的。

Vim中虽然没有自带跳转与补全的功能,但只要结合ctags工具和稍加简单地配置就可以 实现和IDE一样强大的跳转与补全效果。

ctags工具可以解析源代码,把类名、方法名、变量名作为标签存储在tags文件中。这样在 vim中按Control-]就可以跳转到当前光标所在位置的类、方法、变量的定义位置。

但是ctags工具默认是用来给C/C++程序解析标记的,所以需要增加针对Scala的语法规则 才可以解析Scala的源代码。在这里我们把Scala的语法规则添加到配置文件~/.tags中:

--langdef=scala
--langmap=scala:.scala
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private|protected)?[ \t]*class[ \t]+([a-zA-Z0-9_]+)/\4/c,classes/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private|protected)?[ \t]*object[ \t]+([a-zA-Z0-9_]+)/\4/c,objects/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private|protected)?[ \t]*case class[ \t]+([a-zA-Z0-9_]+)/\4/c,case classes/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private|protected)?[ \t]*case object[ \t]+([a-zA-Z0-9_]+)/\4/c,case objects/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*(private|protected)?[ \t]*trait[ \t]+([a-zA-Z0-9_]+)/\4/t,traits/
--regex-scala=/^[ \t]*type[ \t]+([a-zA-Z0-9_]+)/\1/T,types/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*def[ \t]+([a-zA-Z0-9_]+)/\3/m,methods/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*val[ \t]+([a-zA-Z0-9_]+)/\3/l,constants/
--regex-scala=/^[ \t]*((abstract|final|sealed|implicit|lazy)[ \t]*)*var[ \t]+([a-zA-Z0-9_]+)/\3/l,variables/
--regex-scala=/^[ \t]*package[ \t]+([a-zA-Z0-9_.]+)/\1/p,packages/

然后每次在项目的根目录下执行:

ctags -R src --exclude=target --exclude=vendor

ctags就会扫描src目录下的Scala源文件,然后生成tags文件。

现在,可以在vim中按Control-]就可以跳转到当前光标所在位置的类、方法、变量的 定义位置;然后再按Control-T跳回原来的位置。

而代码的补全功能只要按Control-n就可以弹出列表选择匹配的补全了。

Maven构建工具

对于 Scala 来说,官方的构建工具是 SBT。Scala是一门依托于JVM虚拟机的语言, 它最终的编译结果还是Java的字节码文件。所以对于Java程序的工具Maven对于Scala项目 同样适用。

虽然SBT与Maven的功能相同,理论上选择哪一个都无所谓。但是在实际的工作中考虑到 Maven的历史更加长久为了方便与已经存在的Java项目保持一致性,在本项目中依然使用 Maven作为项目的构建工具。 \cite{plain:mavenAct}

对于Maven来说,要实现对Scala的支持无非就是要额外添加两项工作:

  1. 增加Scala的类库。
  2. 增加相关的插件把Scala编译任务加入生命周期。

接下来就可以像构建Java工程一样来构建Scala工程了。

配置ScalaTest测试

在当代软件开发过程中,单元测试的环境必不可少。配套的单元测试用例不仅可以作为 功能完成度的验收标准,在以后的修改过程中也可以用来检验原先的功能是否受到影响。 \cite{plain:ttd}

对于Scala,已经有一套非常成熟的测试工具ScalaTest。在ScalaTest中不仅提供了与 传统Java Junit类似的测试用例编写方案,还有多种其他常用的网络。比如BDD风格( Behavior Driven Development,行为驱动开发)。

在引入ScalaTest依赖时要注意根据当前的Scala版本选择对应的ScalaTest版本。

配置文件的分离

在开发环境与生产环境下,很多参数的配置会有不同。比如说账号、密码、服务器地址 这些参数在生产环境、开发环境、都会有不同的值。所以在构建过程中,有必要把参数 与环境分离出来。 \cite{plain:aglSoft}

比如项目中的服务器ip与端口都在文件example.properties中:

conn.server=${conn.server}
conn.port=${conn.port}

以上配置文件中的参数并没有写上具体的值,而是用占位符${conn.server}${conn.port}来代替。在Maven的构建文件中指定devprd分别对应开发环境和 生产环境:

具体的值根据不同的环境存放在不同的文件中。

比如开发环境中用params-dev.properties

conn.server=127.0.0.1
conn.port=8080

生产环境中用params-prd.properties

conn.server=server.com
conn.port=80

通过mvn resource命令就可以指定所要应用的环境了。

小结

到现在为止,整个开发环境的配置工作已经全部完成。

在文本编辑器方向选择使用了Vim,配合插件可以实现不差于IDE的功能,并且定制化更高 ,可以更好地符合每个用户不同的使用习惯。

在构建工具上,使用了Maven可以实现从库依赖、编译、配置管理、打包发布一系列的工作 极大地提高了工作效率。