Java是一种跨平台的语言,号称:“一次编写,到处运行”,在世界编程语言排行榜中稳居第二名(TIOBE index)。
本文目标是帮助 Java 程序员了解中级程序员应有的水平,避免陷入唯“高技术、新概念”的陷阱。Java 编程入门相对比较容易(推荐:Java入门教程)。学完语法后,很多人希望作一些进阶学习,开始编写网站、访问数据库等应用。然而,当程序比较多时,你又觉得 Java 程序很复杂。因此,我们有必要了解 Java 程序的结构与开发理念,站在项目的高度去看 Java 中级程序员需要哪些知识和能力。
维基百科:约定优于配置是一种软件框架使用的设计範式(原则),旨在减少软件开发人员需做决定的数量,使软件既不失灵活性,且简洁易于理解。
David Heinemeier Hansson 在使用 Ruby on Rails 开发 web 应用时提出这个概念,因为长期做同类项目,项目中程序的组织、命名习惯都会形成最佳实践,遵循这些最佳实践就不仅可以节约开发者沟通时间,而且有利于程序的构建等工作。例如,我们用 MVC 结构去开发 web 程序,程序员都知道控制器类在哪个目录下,完成哪些任务。
我们在第一次学习计算机语言时都会知道概念编码习惯(code conventions)、编程风格(programming idioms)。它们偏向于某种语言的编码规范,例如 C++,Java,Python 都有自己的编码标准,如 google 的编码规范,Pep8编码规范等,这样,IDE 使用这些规范检查你的程序是否符合标准。
约定优于配置偏向于程序的组织于结构,这样编译等工作就可以按约定习惯进行。我们在编译 PyQt 这样的框架时,非常佩服编写 make 脚本的程序员,./configuration
, make
, make install
这些复杂的脚本是如何保证正确的?简化开发过程中各阶段的配置,让应用开发人员集中精力编写业务处理程序(Java 的理念),不用关心如何实现快速交付、部署等问题,则是约定优于配置要解决的问题。
现在的核心问题是要做哪些约定,如何保证约定的实施?
不同领域的应用约定是不一样的,对于 Java 应用则由构建工具(如: Maven,Gradle)负责一些约定的实现,其他就是特定应用框架如 Spring MVC 等示例代码形成的无形习惯。对于一个普通 Java 程序,它的约定是什么?
(1)生成程序框架
在hello-world目录下,输入如下命令:
$ mvn archetype:generate -DarchetypeCatalog=internal -DgroupId=com.mycompany.helloproj -DartifactId=helloworld -Dpackage=com.mycompany.helloworld -Dversion=0.0.1
参考:使用mvn archetype:generate生产maven工程,响应很慢
选默认,回车,回车。
Maven 使用惯例优于配置的原则 。它要求在没有定制之前,所有的项目都有如下的结构:
目录 | 目的 |
---|---|
${basedir} | 存放 pom.xml |
${basedir}/src/main/java | 项目的 java源代码 |
${basedir}/src/main/resources | 项目的资源,比如说 property文件、图片等 |
${basedir}/src/test/java | 项目的测试类,比如说 JUnit代码 |
${basedir}/src/test/resources | 测试使用的资源 |
使用 IntelliJ IDEA 导入项目,就可以看到这样的程序结构:
(2)约定应用与组织规范
举例说明,Java 访问资源文件如 xxx.xml,如果写成:classpath:/xxx.xml
,这个类一定默认加载main/resources/xxx.xml
。
类似的应用约定很多,学习的办法只有一个,积累!积累!积累!,见多识广。
再看 pom.xml 文件内容:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.mycompany.helloprojgroupId>
<artifactId>helloworldartifactId>
<version>0.0.1version>
<packaging>jarpackaging>
<name>helloworldname>
<url>http://maven.apache.orgurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>3.8.1version>
<scope>testscope>
dependency>
dependencies>
project>
在 pom.xml 所在目录下,输入:
$ mvn package
我们看到 maven 项目做了许多工作:
结果,在 target 目录下,出现了 helloworld-0.0.1.jar
文件。
$ mvn install
结果,在 $USER_HOME/.m2/repository/com/mycompany/helloproj/helloworld/0.0.1/ 出现了 helloworld-0.0.1.jar
文件。
在 Maven 配置中, groupId:artifactId:packaging:version 四个元素唯一表示了项目的部件、版本、发布方式。
又称为项目坐标。
通常,合理的命名展示了一般公司项目的管理结构。
通过结构约定,我们定义了企业项目管理的基本方式。
(3)约定开发流程
maven 能支持项目开发不同的生命周期阶段 (lifecycle phase )。但是最常用的是默认的 Maven生命周期 (default Maven lifecycle )。一个 Jar 包的阶段包括:
每个阶段有默认目标。 maven 命令就是 mvn goal ,常用目标有:
最常见命令之一就是:
$ mvn clean package install
通过对 jar, war, ear 等工程流程(阶段)的约定,简单的配置、简单的命令就实现了开发过程自动化,
程序员仅需关注业务。
(4)扩展任务
约定了开发阶段(这也是有人认为 Maven 不灵活的因素),通过插件(plug in)就在不同阶段基础上扩展新目标。
例如:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojogroupId>
<artifactId>exec-maven-pluginartifactId>
<version>1.2.1version>
<executions>
<execution>
<goals>
<goal>javagoal>
goals>
execution>
executions>
<configuration>
<mainClass>com.mycompany.helloworld.AppmainClass>
configuration>
plugin>
plugins>
build>
在 pom.xml 中添加 exec-maven-plugin 插件,就可以用 mvn exec:java
执行程序。
参见: Exec Maven Plugin使用
如果你需要打包成可执行的 jar,jar 和 assembly 插件最常用,参见
本文不是 maven 的教程,只是说明通过约定一些基本规则,程序开发编程更加简单、高效,更易于多人协同,共享成果。
测试驱动的开发是敏捷开发(短周期迭代)的一种生产实践,它要求程序的功能或特性(需求)在开发出来的同时,测试案例(程序)同步完成,并用于需求验证。
概念起源于 Kent Beck 的【极限编程/Extreme Programming】实践。如果你没有读过,越忙越要抽空读。Kent Beck是项目经理出身,所以也知道你很忙,所以书的很薄,你必定有许多共鸣的。Kent 喜欢讲故事,怎么做就让你自己领会了,【硝烟中的scrum和xp】书可以帮你。
Kent Beck 发现,在错误(bug 可能来源于业务逻辑,也可能是结构设计)比较多的程序上迭代,积累偏差会迅速放大,导致程序越来越难以调试与维护。因此,需要低成本的手段保障每次迭代产品的品质,于是提出了 TDD 的概念。
假设一个中型项目按特定的结构建设,这个结构包含两个方面:
假设这个中型项目划分为如下依赖关系的部件:
集成测试过程如下:
这样,每个部件程序员不仅要编写业务程序,也需要负责使用 Junit 或 TestNG 测试自己的模块。
测试程序员仅编写集成测试,测试部件组合后的正确性。实际测试中会复杂一些,会使用 mock 测试技术。
由于这样的测试可以每天晚上用 shell 脚本执行,所以也成为 daily building 或 “冒烟测试”
参考:基于Maven的持续集成实践
快速交付指按固定迭代周期(2-4 周)将产品交付客户试用,及时获取客户反馈。
所以,每个迭代需要花2-3天做产品系统测试。
在没有 TDD 之前,项目进展都是通过程序的日报/周报等方式评估的。项目经理从程序员获取信息,然后做项目进度图。
开始,项目进展非常快,%20,%40,%60,%80,%90,%95,%99 。。。延期,再延期。
TDD 改变了这一切,每个人每天收到报告,有多少测试没通过?已通过 xx%。显式度量调动了程序员积极性。
测试数据与程序员分离,有力提升 bug 发现的能力。
TDD 也不是万能的,对于 10 几个开发者的团队,那 1-2 核心程序员才是品质的保证。他们完成绝大多数代码,
TDD 对这些人驱动的意义不大,TDD 成本高于仅做系统测试。
本文虽然仅讨论了约定优于配置(CoC)和测试驱动的开发(TDD),能实施这两点的程序猿一定在中级或以上。
因为,掌握 CoC 和 TDD 对于一个团队来说意味着:
这证明中级程序猿有能力做一个小型开发团队(3~7人)的技术经理。因为精确度量,领导喜欢你;
因为架构和进度的优良控制,程序猿也省心,特别是哪些喜欢与计算机打交道的天才会成为你的中坚;
PPT程序猿会自动离开。
掌握 CoC 和 TDD 本质是就是 Java 开发团队具体实施敏捷开发实践的要点,它满足敏捷四条宣言:
在 hello world 这样规模的程序上实践,你是不可能完全理解 CoC、TDD的重要性的。
掌握一个应用领域程序的逻辑结构与部署结构,组织每天测试在实践中会有无数障碍(不做不知道)。
要成为合格的中级程序员,需要进一步努力,例如: