面向Java开发人员的Scala指南: 构建计算器,第1 部分

Python015

面向Java开发人员的Scala指南: 构建计算器,第1 部分,第1张

摘要 特定于领域的语言已经成为一个热门话题 很多函数性语言之所以受欢迎 主要是因为它们可以用于构建特定于领域的语言 鉴于此 在 面向 Java? 开发人员的 Scala 指南 系列的第 篇文章中 Ted Neward 着手构建一个简单的计算器 DSL 以此来展示函数性语言的构建 外部 DSL 的强大功能 他研究了 Scala 的一个新的特性 case 类 并重新审视一个功能强大的特性 模式匹配

上个月的文章发表后 我又收到了一些抱怨/评论 说我迄今为止在本系列中所用的示例都没涉及到什么实质性的问题 当然在学习一个新语言的初期使用一些小例子是很合理的 而读者想要看到一些更 现实的 示例 从而了解语言的深层领域和强大功能以及其优势 这也是理所当然的 因此 在这个月的文章中 我们来分两部分练习构建特定于领域的语言(DSL)— 本文以一个小的计算器语言为例

关于本系列

Ted Neward 将和您一起深入探讨 Scala 编程语言 在这个新的 developerWorks 系列 中 您将深入了解 Sacla 并在实践中看到 Scala 的语言功能 进行比较时 Scala 代码和 Java 代码将放在一起展示 但(您将发现)Scala 中的许多内容与您在 Java 编程中发现的任何内容都没有直接关联 而这正是 Scala 的魅力所在!如果用 Java 代码就能够实现的话 又何必再学习 Scala 呢?

特定于领域的语言

可能您无法(或没有时间)承受来自于您的项目经理给您的压力 那么让我直接了当地说吧 特定于领域的语言无非就是尝试(再一次)将一个应用程序的功能放在它该属于的地方 — 用户的手中

通过定义一个新的用户可以理解并直接使用的文本语言 程序员成功摆脱了不停地处理 UI 请求和功能增强的麻烦 而且这样还可以使用户能够自己创建脚本以及其他的工具 用来给他们所构建的应用程序创建新的行为 虽然这个例子可能有点冒险(或许会惹来几封抱怨的电子邮件) 但我还是要说 DSL 的最成功的例子就是 Microsoft® Office Excel 语言 用于表达电子表格单元格的各种计算和内容 甚至有些人认为 SQL 本身就是 DSL 但这次是一个旨在与关系数据库相交互的语言(想象一下如果程序员要通过传统 API read() / write() 调用来从 Oracle 中获取数据的话 那将会是什么样子)

这里构建的 DSL 是一个简单的计算器语言 用于获取并计算数学表达式 其实 这里的目标是要创建一个小型语言 这个语言能够允许用户来输入相对简单的代数表达式 然后这个代码来为它求值并产生结果 为了尽量简单明了 该语言不会支持很多功能完善的计算器所支持的特性 但我不也不想把它的用途限定在教学上 — 该语言一定要具备足够的可扩展性 以使读者无需彻底改变该语言就能够将它用作一个功能更强大的语言的核心 这意味着该语言一定要可以被轻易地扩展 并要尽量保持封装性 用起来不会有任何的阻碍

关于 DSL 的更多信息

DSL 这个主题的涉及面很广 它的丰富性和广泛性不是本文的一个段落可以描述得了的 想要了解更多 DSL 信息的读者可以查阅本文末尾列出的 Martin Fowler 的 正在进展中的图书 特别要注意关于 内部 和 外部 DSL 之间的讨论 Scala 以其灵活的语法和强大的功能而成为最强有力的构建内部和外部 DSL 的语言

换句话说 (最终的)目标是要允许客户机编写代码 以达到如下的目的

清单 计算器 DSL 目标

// This is Java using the Calculator String s = (( * ) + ) double result = tedneward calcdsl Calculator evaluate(s)System out println( We got + result)// Should be

我们不会在一篇文章完成所有的论述 但是我们在本篇文章中可以学习到一部分内容 在下一篇文章完成全部内容

从实现和设计的角度看 可以从构建一个基于字符串的解析器来着手构建某种可以 挑选每个字符并动态计算 的解析器 这的确极具诱惑力 但是这只适用于较简单的语言 而且其扩展性不是很好 如果语言的目标是实现简单的扩展性 那么在深入研究实现之前 让我们先花点时间想一想如何设计语言

根据那些基本的编译理论中最精华的部分 您可以得知一个语言处理器(包括解释器和编译器)的基本运算至少由两个阶段组成

●  解析器 用于获取输入的文本并将其转换成 Abstract Syntax Tree(AST)     ●  代码生成器(在编译器的情况下) 用于获取 AST 并从中生成所需字节码 或是求值器(在解释器的情况下) 用于获取 AST 并计算它在 AST 里面所发现的内容

拥有 AST 就能够在某种程度上优化结果树 如果意识到这一点的话 那么上述区别的原因就变得更加显而易见了 对于计算器 我们可能要仔细检查表达式 找出可以截去表达式的整个片段的位置 诸如在乘法表达式中运算数为 的位置(它表明无论其他运算数是多少 运算结果都会是 )

您要做的第一件事是为计算器语言定义该 AST 幸运的是 Scala 有 case 类 一种提供了丰富数据 使用了非常薄的封装的类 它们所具有的一些特性使它们很适合构建 AST

case 类

在深入到 AST 定义之前 让我先简要概述一下什么是 case 类 case 类是使 scala 程序员得以使用某些假设的默认值来创建一个类的一种便捷机制 例如 当编写如下内容时

清单 对 person 使用 case 类

case class Person(first:String last:String age:Int){}

Scala 编译器不仅仅可以按照我们对它的期望生成预期的构造函数 — Scala 编译器还可以生成常规意义上的 equals() toString() 和 hashCode() 实现 事实上 这种 case 类很普通(即它没有其他的成员) 因此 case 类声明后面的大括号的内容是可选的

清单 世界上最短的类清单

case class Person(first:String last:String age:Int)

这一点通过我们的老朋友 javap 很容易得以验证

清单 神圣的代码生成器 Batman!

C:\Projects\Exploration\Scala>javap PersonCompiled from case scala public class Person extends java lang Object implements scala ScalaObject scala Product java io Serializable{    public Person(java lang String java lang String int)    public java lang Object productElement(int)    public int productArity()    public java lang String productPrefix()    public boolean equals(java lang Object)    public java lang String toString()    public int hashCode()    public int $tag()    public int age()    public java lang String last()    public java lang String first()}

如您所见 伴随 case 类发生了很多传统类通常不会引发的事情 这是因为 case 类是要与 Scala 的模式匹配(在 集合类型 中曾简短分析过)结合使用的

使用 case 类与使用传统类有些不同 这是因为通常它们都不是通过传统的 new 语法构造而成的 事实上 它们通常是通过一种名称与类相同的工厂方法来创建的

清单 没有使用 new 语法?

object App{  def main(args : Array[String]) : Unit =  {    val ted = Person( Ted Neward )  }}

case 类本身可能并不比传统类有趣 或者有多么的与众不同 但是在使用它们时会有一个很重要的差别 与引用等式相比 case 类生成的代码更喜欢按位(biise)等式 因此下面的代码对 Java 程序员来说有些有趣的惊喜

清单 这不是以前的类

object App{  def main(args : Array[String]) : Unit =  {    val ted = Person( Ted Neward )    val ted = Person( Ted Neward )    val amanda = Person( Amanda Laucher )    System out println( ted == amanda: +      (if (ted == amanda) Yes else No ))    System out println( ted == ted: +      (if (ted == ted) Yes else No ))    System out println( ted == ted : +      (if (ted == ted ) Yes else No ))  }}/*C:\Projects\Exploration\Scala>scala Appted == amanda: Noted == ted: Yested == ted : Yes*/

case 类的真正价值体现在模式匹配中 本系列的读者可以回顾一下模式匹配(参见 本系列的第二篇文章 关于 Scala 中的各种控制构造) 模式匹配类似 Java 的 switch/case 只不过它的本领和功能更加强大 模式匹配不仅能够检查匹配构造的值 从而执行值匹配 还可以针对局部通配符(类似局部 默认值 的东西)匹配值 case 还可以包括对测试匹配的保护 来自匹配标准的值还可以绑定于局部变量 甚至符合匹配标准的类型本身也可以进行匹配

有了 case 类 模式匹配具备了更强大的功能 如清单 所示

清单 这也不是以前的 switch

case class Person(first:String last:String age:Int)object App{  def main(args : Array[String]) : Unit =  {    val ted = Person( Ted Neward )    val amanda = Person( Amanda Laucher )    System out println(process(ted))    System out println(process(amanda))  }  def process(p : Person) =  {    Processing + p + reveals that +    (p match    {      case Person(_ _ a) if a >=>        they re certainly old       case Person(_ Neward _) =>        they e from good genes       case Person(first last ageInYears) if ageInYears >=>        first + + last + is + ageInYears + years old       case _ =>        I have no idea what to do with this person     })  }}/*C:\Projects\Exploration\Scala>scala AppProcessing Person(Ted Neward ) reveals that they re certainly old Processing Person(Amanda Laucher ) reveals that Amanda Laucher is years old */

清单 中发生了很多操作 下面就让我们先慢慢了解发生了什么 然后回到计算器 看看如何应用它们

首先 整个 match 表达式被包裹在圆括号中 这并非模式匹配语法的要求 但之所以会这样是因为我把模式匹配表达式的结果根据其前面的前缀串联了起来(切记 函数性语言里面的任何东西都是一个表达式)

其次 第一个 case 表达式里面有两个通配符(带下划线的字符就是通配符) 这意味着该匹配将会为符合匹配的 Person 中那两个字段获取任何值 但是它引入了一个局部变量 a p age 中的值会绑定在这个局部变量上 这个 case 只有在同时提供的起保护作用的表达式(跟在它后边的 if 表达式)成功时才会成功 但只有第一个 Person 会这样 第二个就不会了 第二个 case 表达式在 Person 的 firstName 部分使用了一个通配符 但在 lastName 部分使用常量字符串 Neward 来匹配 在 age 部分使用通配符来匹配

由于第一个 Person 已经通过前面的 case 匹配了 而且第二个 Person 没有姓 Neward 所以该匹配不会为任何一个 Person 而被触发(但是 Person( Michael Neward ) 会由于第一个 case 中的 guard 子句失败而转到第二个 case)

第三个示例展示了模式匹配的一个常见用途 有时称之为提取 在这个提取过程中 匹配对象 p 中的值为了能够在 case 块内使用而被提取到局部变量中(第一个 最后一个和 ageInYears) 最后的 case 表达式是普通 case 的默认值 它只有在其他 case 表达式均未成功的情况下才会被触发

简要了解了 case 类和模式匹配之后 接下来让我们回到创建计算器 AST 的任务上

计算器 AST

首先 计算器的 AST 一定要有一个公用基类型 因为数学表达式通常都由子表达式组成 通过 + ( * ) 就可以很容易地看到这一点 在这个例子中 子表达式 ( * ) 将会是 + 运算的右侧运算数

事实上 这个表达式提供了三种 AST 类型

●  基表达式    ●  承载常量值的 Number 类型    ●  承载运算和两个运算数的 BinaryOperator

想一下 算数中还允许将一元运算符用作求负运算符(减号) 将值从正数转换为负数 因此我们可以引入下列基本 AST

清单 计算器 AST(src/calc scala)

package tedneward calcdsl{  private[calcdsl] abstract class Expr  private[calcdsl]  case class Number(value : Double) extends Expr  private[calcdsl]  case class UnaryOp(operator : String arg : Expr) extends Expr  private[calcdsl]  case class BinaryOp(operator : String left : Expr right : Expr)   extends Expr}

注意包声明将所有这些内容放在一个包( tedneward calcdsl)中 以及每一个类前面的访问修饰符声明表明该包可以由该包中的其他成员或子包访问 之所以要注意这个是因为需要拥有一系列可以测试这个代码的 JUnit 测试 计算器的实际客户机并不一定非要看到 AST 因此 要将单元测试编写成 tedneward calcdsl 的一个子包

清单 计算器测试(testsrc/calctest scala)

package tedneward calcdsl test{  class CalcTest  {    import junit _ Assert _        @Test def ASTTest =    {      val n = Number( )      assertEquals( n value)    }        @Test def equalityTest =    {      val binop = BinaryOp( + Number( ) Number( ))            assertEquals(Number( ) binop left)      assertEquals(Number( ) binop right)      assertEquals( + binop operator)    }  }}

到目前为止还不错 我们已经有了 AST

再想一想 我们用了四行 Scala 代码构建了一个类型分层结构 表示一个具有任意深度的数学表达式集合(当然这些数学表达式很简单 但仍然很有用) 与 Scala 能够使对象编程更简单 更具表达力相比 这不算什么(不用担心 真正强大的功能还在后面)

接下来 我们需要一个求值函数 它将会获取 AST 并求出它的数字值 有了模式匹配的强大功能 编写这样的函数简直轻而易举

清单 计算器(src/calc scala)

package tedneward calcdsl{  //   object Calc  {    def evaluate(e : Expr) : Double =    {      e match {        case Number(x) =>x        case UnaryOp( x) =>(evaluate(x))        case BinaryOp( + x x ) =>(evaluate(x ) + evaluate(x ))        case BinaryOp( x x ) =>(evaluate(x ) evaluate(x ))        case BinaryOp( * x x ) =>(evaluate(x ) * evaluate(x ))        case BinaryOp( / x x ) =>(evaluate(x ) / evaluate(x ))      }    }  }}

注意 evaluate() 返回了一个 Double 它意味着模式匹配中的每一个 case 都必须被求值成一个 Double 值 这个并不难 数字仅仅返回它们的包含的值 但对于剩余的 case(有两种运算符) 我们还必须在执行必要运算(求负 加法 减法等)前计算运算数 正如常在函数性语言中所看到的 会使用到递归 所以我们只需要在执行整体运算前对每一个运算数调用 evaluate() 就可以了

大多数忠实于面向对象的编程人员会认为在各种运算符本身以外 执行运算的想法根本就是错误的 — 这个想法显然大大违背了封装和多态性的原则 坦白说 这个甚至不值得讨论 这很显然违背 了封装原则 至少在传统意义上是这样的

在这里我们需要考虑的一个更大的问题是 我们到底从哪里封装代码?要记住 AST 类在包外是不可见的 还有就是客户机(最终)只会传入它们想求值的表达式的一个字符串表示 只有单元测试在直接与 AST case 类合作

但这并不是说所有的封装都没有用了或过时了 事实上恰好相反 它试图说服我们在对象领域所熟悉的方法之外 还有很多其他的设计方法也很奏效 不要忘了 Scala 兼具对象和函数性 有时候 Expr 需要在自身及其子类上附加其他行为(例如 实现良好输出的 toString 方法) 在这种情况下可以很轻松地将这些方法添加到 Expr 函数性和面向对象的结合提供了另一种选择 无论是函数性编程人员还是对象编程人员 都不会忽略到另一半的设计方法 并且会考虑如何结合两者来达到一些有趣的效果

从设计的角度看 有些其他的选择是有问题的 例如 使用字符串来承载运算符就有可能出现小的输入错误 最终会导致结果不正确 在生产代码中 可能会使用(也许必须使用)枚举而非字符串 使用字符串的话就意味着我们可能潜在地 开放 了运算符 允许调用出更复杂的函数(诸如 abs sin cos tan 等)乃至用户定义的函数 这些函数是基于枚举的方法很难支持的

对所有设计和实现的来说 都不存在一个适当的决策方法 只能承担后果 后果自负

但是这里可以使用一个有趣的小技巧 某些数学表达式可以简化 因而(潜在地)优化了表达式的求值(因此展示了 AST 的有用性)

●  任何加上 的运算数都可以被简化成非零运算数     ●  任何乘以 的运算数都可以被简化成非零运算数     ●  任何乘以 的运算数都可以被简化成零

不止这些 因此我们引入了一个在求值前执行的步骤 叫做 simplify() 使用它执行这些具体的简化工作

清单 计算器(src/calc scala)

    def simplify(e : Expr) : Expr =    {      e match {        // Double negation returns the original value        case UnaryOp( UnaryOp( x)) =>x        // Positive returns the original value        case UnaryOp( + x) =>x        // Multiplying x by returns the original value        case BinaryOp( * x Number( )) =>x        // Multiplying by x returns the original value        case BinaryOp( * Number( ) x) =>x        // Multiplying x by returns zero        case BinaryOp( * x Number( )) =>Number( )        // Multiplying by x returns zero        case BinaryOp( * Number( ) x) =>Number( )        // Dividing x by returns the original value        case BinaryOp( / x Number( )) =>x        // Adding x to returns the original value        case BinaryOp( + x Number( )) =>x        // Adding to x returns the original value        case BinaryOp( + Number( ) x) =>x        // Anything else cannot (yet) be simplified        case _ =>e      }    }

还是要注意如何使用模式匹配的常量匹配和变量绑定特性 从而使得编写这些表达式可以易如反掌 对 evaluate() 惟一一个更改的地方就是包含了在求值前先简化的调用

清单 计算器(src/calc scala)

    def evaluate(e : Expr) : Double =    {      simplify(e) match {        case Number(x) =>x        case UnaryOp( x) =>(evaluate(x))        case BinaryOp( + x x ) =>(evaluate(x ) + evaluate(x ))        case BinaryOp( x x ) =>(evaluate(x ) evaluate(x ))        case BinaryOp( * x x ) =>(evaluate(x ) * evaluate(x ))        case BinaryOp( / x x ) =>(evaluate(x ) / evaluate(x ))      }    }

还可以再进一步简化 注意一下 它是如何实现只简化树的最底层的?如果我们有一个包含 BinaryOp( * Number( ) Number( )) 和 Number( ) 的 BinaryOp 的话 那么内部的 BinaryOp 就可以被简化成 Number( ) 但外部的 BinaryOp 也会如此 这是因为此时外部 BinaryOp 的其中一个运算数是零

我突然犯了作家的职业病了 所以我想将它留予读者来定义 其实是想增加点趣味性罢了 如果读者愿意将他们的实现发给我的话 我将会把它放在下一篇文章的代码分析中 将会有两个测试单元来测试这种情况 并会立刻失败 您的任务(如果您选择接受它的话)是使这些测试 — 以及其他任何测试 只要该测试采取了任意程度的 BinaryOp 和 UnaryOp 嵌套 — 通过

结束语

显然我还没有说完 还有分析的工作要做 但是计算器 AST 已经成形 我们无需作出大的变动就可以添加其他的运算 运行 AST 也无需大量的代码(按照 Gang of Four 的 Visitor 模式) 而且我们已经有了一些执行计算本身的工作代码(如果客户机愿意为我们构建用于求值的代码的话)

lishixinzhi/Article/program/Java/hx/201311/25735

就我个人来看,如果你C语言、数据结构和算法学得好(当然好的定义很宽泛,我是指对C语法理解透彻,对数列、堆栈、链表都比较熟悉,可以独立编写可运行的小型系统,比如什么图书管理系统和连接数据库的小程序),学Java不是什么难事,关键是对面向对象概念的理解,多些对照书本联系调试吧,我觉得希望出版社的《java程序员开发指南》这本书不错。

再学学用JSP写些简单页面,开始尽量不要使用太复杂的IDE,Eclipse还是等熟悉后开发项目再用吧,先用EditPlus等编写比较好,可以锻炼你自己调试的能力。当然JavaScript也是必学的,再练习连接数据库并熟悉各种操作。建议先用sql2000或mysql,熟悉再学oracle。

以上基本完成后,再寻找机会做一个大点的项目,哪怕只是几个模块。将它当今后工作来做,认真写好开发文档,包扩需求文档,概要设计,详细设计,这对你今后工作积累经验是一个好的锻炼机会。这时候可以尝试用MVC模式和使用一些框架,Strust,Hibernate,Spring和一些新工具Eclipse,JBuilder,NetBean等还有用项目管理工具,如CVS,这对增强自身竞争力很有好处。

具体什么书要因人而异,一开始看《Java编程思想》是不切实际的,看得明白又容易理解,感觉对自己有帮助的就是好书。我主要提供一个学习流程的思路供你参考,具体如何学还要自己边学边摸索,希望对你有用吧!

一、Java编程入门类

对于没有Java编程经验的程序员要入门,随便读什么入门书籍都一样,这个阶段需要你快速的掌握Java基础语法和基本用法,宗旨就是“囫囵吞枣不求甚解”,先对Java熟悉起来再说。用很短的时间快速过一遍Java语法,连懵带猜多写写代码,要“知其然”。

1、《Java编程思想》

在有了一定的Java编程经验之后,你需要“知其所以然”了。这个时候《Java编程思想》是一本让你知其所以然的好书,它对于基本的面向对象知识有比较清楚的交待,对Java基本语法,基本类库有比较清楚的讲解,可以帮你打一个良好的Java编程基础。这本书的缺点是实在太厚,也比较罗嗦,不适合现代人快节奏学习,因此看这本书要懂得取舍,不是每章每节都值得一看的,挑重点的深入看就可以了。

2、《Agile Java》中文版

这本书是出版社送给我的,我一拿到就束之高阁,放在书柜一页都没有翻过,但是前两天整理书柜的时候,拿出来一翻,竟然发现这绝对是一本好书!这本书一大特点是以单元测试和TDD来贯穿全书的,在教你Java各种重要的基础知识的过程中,潜移默化的影响你的编程思维走向敏捷,走向TDD。另外这本书成书很新,以JDK5.0的语法为基础讲解,要学习JDK5.0的新语法也不错。还有这本书对于内容取舍也非常得当,Java语言毕竟类库庞大,可以讲的内容太多,这本书选择的内容以及内容的多寡都很得当,可以让你以最少的时间掌握Java最重要的知识,顺便培养出来优秀的编程思路,真是一本不可多得的好书。 虽然作者自己把这本书定位在入门级别,但我不确定这本书用来入门是不是稍微深了点,我自己也准备有空的时候翻翻这本书,学习学习。

二、Java编程进阶类

打下一个良好的Java基础,还需要更多的实践经验积累,我想没有什么捷径。有两本书值得你在编程生涯的这个阶段阅读,培养良好的编程习惯,提高你的代码质量。

1、《重构 改善既有代码的设计》

这本书名气很大,不用多介绍,可以在闲暇的时候多翻翻,多和自己的实践相互印证。这本书对你产生影响是潜移默化的。

2、《测试驱动开发 by Example》

本书最大特点是很薄,看起来没有什么负担。你可以找一个周末的下午,一边看,一边照做,一个下午就把书看完,这本书的所有例子跑完了。这本书的作用是通过实战让你培养TDD的思路。

三、Java架构师之路

到这个阶段,你应该已经非常娴熟的运用Java编程,而且有了一个良好的编程思路和习惯了,但是你可能还缺乏对应用软件整体架构的把握,现在就是你迈向架构师的第一步。

1、《Expert One-on-One J2EE Design and Development》

这本书是Rod Johnson的成名著作,非常经典,从这本书中的代码诞生了springframework。但是好像这本书没有中译本。

2、《Expert One-on-One J2EE Development without EJB》

这本书由gigix组织翻译,多位业界专家参与,虽然署名译者是JavaEye,其实JavaEye出力不多,实在是忝居译者之名。

以上两本书都是Rod Johnson的经典名著,Java架构师的必读书籍。在我所推荐的这些书籍当中,是我看过的最仔细,最认真的书,我当时读这本书几乎是废寝忘食的一气读完的,有小时候挑灯夜读金庸武侠小说的劲头,书中所讲内容和自己的经验知识一一印证,又被无比精辟的总结出来,读完这本书以后,我有种被打通经脉,功力爆增的感觉。

但是后来我看过一些其他人的评价,似乎阅读体验并没有我那么high,也许是因为每个人的知识积累和经验不同导致的。我那个时候刚好是经验知识积累已经足够丰富,但是还没有系统的整理成型,让这本书一梳理,立刻形成完整的知识体系了。

3、《企业应用架构模式》

Martin的又一本名著,但这本书我只是泛泛的看了一遍,并没有仔细看。这本书似乎更适合做框架的人去看,例如如果你打算自己写一个ORM的话,这本书是一定要看的。但是做应用的人,不看貌似也无所谓,但是如果有空,我还是推荐认真看看,会让你知道框架为什么要这样设计,这样你的层次可以晋升到框架设计者的角度去思考问题。Martin的书我向来都是推崇,但是从来都没有像Rod Johnson的书那样非常认真去看。

4、《敏捷软件开发 原则、模式与实践》

Uncle Bob的名著,敏捷的经典名著,这本书比较特别,与其说是讲软件开发过程的书,不如说讲软件架构的书,本书用了很大篇幅讲各种面向对象软件开发的各种模式,个人以为看了这本书,就不必看GoF的《设计模式》了。

四、软件开发过程

了解软件开发过程不单纯是提高程序员个人的良好编程习惯,也是增强团队协作的基础。

1、《UML精粹》

UML其实和软件开发过程没有什么必然联系,却是软件团队协作沟通,撰写软件文档需要的工具。但是UML真正实用的图不多,看看这本书已经足够了,完全没有必要去啃《UML用户指南》之类的东西。要提醒大家的是,这本书的中译本翻译的非常之烂,建议有条件的看英文原版。

2、《解析极限编程 拥抱变化》XP

这是Kent Beck名著的第二版,中英文对照。没什么好说的,必读书籍。

3、《统一软件开发过程》UP

其实UP和敏捷并不一定冲突,UP也非常强调迭代,测试,但是UP强调的文档和过程驱动却是敏捷所不取的。不管怎么说,UP值得你去读,毕竟在中国真正接受敏捷的企业很少,你还是需要用UP来武装一下自己的,哪怕是披着UP的XP。

4、《敏捷建模》AM

Scott Ambler的名著,这本书非常的progmatic,告诉你怎么既敏捷又UP,把敏捷和UP统一起来了,又提出了很多progmatic的建议和做法。你可以把《解析极限编程 拥抱变化》、《统一软件开发过程》和《敏捷建模》这三本书放在一起读,看XP和UP的不同点,再看AM是怎么统一XP和UP的,把这三种理论融为一炉,形成自己的理论体系,那么你也可以去写书了。

五、软件项目管理

如果你突然被领导提拔为项目经理,而你完全没有项目管理经验,你肯定会心里没底;如果你觉得自己管理项目不善,很想改善你的项目管理能力,那么去考PMP肯定是远水不解近渴的。

1、《快速软件开发》

这也是一本名著。可以这样说,有本书在手,你就有了一个项目管理的高级参谋给你出谋划策,再也不必担心自己不能胜任的问题了。这本书不是讲管理的理论的,在实际的项目管理中,讲这些理论是不解决问题的,这本书有点类似于“软件项目点子大全”之类的东西,列举了种种软件项目当中面临的各种问题,以及应该如何解决问题的点子,你只需要稍加变通,找方抓药就行了。

六、总结

在这份推荐阅读书籍的名单中,我没有列举流行的软件框架类学习书籍,例如Struts,Hibernate,Spring之类,也没有列举AJAX方面的书籍。是因为这类书籍容易过时,而上述的大半书籍的生命周期都足够长,值得你去购买和收藏。

希望对您有所帮助!~