2008年12月16日星期二

关于软件过程的比较



关于软件管理过程,目前敏捷过程最为热门。主要的敏捷过程有以下几种:XP 极限编程, SCRUM, FDD 特征驱动开发, DDD 领域建模驱动开发, ICONIX, ASD 自适应, CRYSTAL 水晶, DSDM, TDD 测试驱动开发, AMDD 敏捷建模。再加上“传统”的RUP。
浏览这些过程会发现这些敏捷过程所涉及的领域还是有所区别的。简单来讲,可以粗略的划分为管理过程设计过程。例如:
  • SCRUM:很明显是管理过程。在实践框架中提供了一些可以明确执行的实践指南。而其中对于设计方法或者过程基本没有什么要求。
  • FDD:应该属于管理过程。其中明确了对项目的进行如何规划。而对设计方法所述比较少。
  • ASD, CRYSTAL:属于管理过程,其中重点讨论对人的管理理念。管理框架具有很大的灵活性。
  • DDD:属于设计过程,重点描述如何从业务需求导出到系统设计编码的过程。
  • AMDD:属于设计过程。
  • XP:比较特殊,不是完整的管理体系。而对设计过程也很少描述,仅仅提供了一些最佳实践。由于其要求非常明确,所以我觉得更接近与设计过程。
  • TDD:应该不算是过程,仅仅是一种辅助设计的方法。所以经常被其它敏捷过程所引用。
  • ICONIX:对管理过程与设计过程都有所涉及。不过更加接近于设计过程。
  • DSDM:不太了解,无法分类。
基于此原因,所以经常可以看到将两种或者以上的敏捷过程组合进行应用的现象。如:SCRUM+XP, FDD+DDD等等。而AMDD和TDD更是经常被其他过程所引用。
不过这也侧面说明,这些敏捷过程往往内容都不够完整,或者说是针对性比较强。

现 在看来,敏捷过程的宣传者针对的往往是一些传统的重型过程,如CMM, ISO等等。而由于RUP内容比较复杂,所以往往也被算进来。这其实是由于对RUP的误解造成的。RUP被设计为统一过程,所以必然包罗万象,以便适应于 更多类型的项目。这就造成了RUP的复杂性,其实也说明了RUP内容的丰富。但是,当具体到某一个项目的时候,必然会根据项目特点或者企业环境因素对 RUP过程进行裁剪,以形成特色的过程。所以针对小型项目,RUP完全可能变成为非常“敏捷”的过程。
不过这也属于一个矛盾:1. 对于小型项目的项目经理来讲基本没有精力或者经验对整个RUP体系进行完整的研究。2. 如果缺乏对RUP体系的研究,就不可能对RUP体系进行恰当的裁剪。
基于这个矛盾,出现了很多伪RUP过程实践。
这样的现象是因为RUP中缺乏对一些比较典型的项目类型的具体指导和模版,来使项目经理可以快速的对RUP开始实践。而在这方面敏捷过程往往做得更好。例如XP过程,提出了明确的原则和实践指南,使得人可以非常简单清晰的学习和实践练习。
基于此,例如XP这样的过程就在程序员中很受欢迎,因为程序员多数项目管理经验不够充足,所有如果有简单而明确的实践规则的话,当然更容易付诸实施。
但 反过来讲,这些敏捷过程对项目类型的要求往往非常严格,例如,XP过程就会要求:项目组规模、办公地点、用户现场、合同类型等等。所以当人们将它应用与不 同的领域时又不得不对它进行改良或者采用其它的软件过程。在这方面RUP就会好很多,深入研究RUP可以使你用一种软件过程应对更多类型的项目
ICONIX是所以受欢迎,就是因为它延续了RUP的思路而有提供了可明确参考的工作步骤。

当然反过来,对敏捷过程的误解也是存在的,例如:RUP的支持者往往认为XP过程没有设计。实际情况是XP过程也是相当重视设计的,只不过方式不同。XP过程不建议进行预先设计,而是使用其它的一些辅助手段保证软件设计的优化,例如TDD、重构等等。

再看看思维方式。新兴的敏捷过程往往宣传自己基于小型迭代,并以此来对应变更,使项目满足客户业务需求。但所谓迭代一词应该更加细分成:“增量开发”与“迭代开发”,之间的区别如图所示。

由此看来,如XP, SCRUM, FDD等过程其实都属于增量开发。而RUP和ICONIX过程属于迭代开发。所以,对此概念模糊的人就会误解RUP更像瀑布模式。
其实增量与迭代无所谓优劣,仅仅是思维方式的不同。

2008年12月8日星期一

关于系统设计的文章摘抄

模型(Model)代表应用程序的数据(data)和用于控制访问和修改这些数据的业务规则(business rule)。通常模型被用来作为对现实世界中一个处理过程的软件近似。
视图(View)被用来组织模型的内容,它从模型那里获得数据并指定这些数据如何表现。
控制器(Controller)定义了应用程序的行为;它负责对来自视图的用户要求进行解释,并把这些要求映射成相应的行为,这些行为由模型负责实现。

有人提到
MVC模式时说MVC代表了模型层、视图层、控制层,我觉得这是不对的。在经典的J2EE三层架构中,三层是分为Web层、业务层、持久化层;这个经典分层是基于分布式应用(EJB)的,也就说,Web层物理上是在Web服务器中, 业务层和持久化层物理上是在应用服务器中。在这种情况下,MVC只是属于Web层这一层的,而不是分为三层。在这种分布式应用中,视图就是JSP(如果采用的话),控制器就是Servlet(如果采用的话),而模型就是就是调用业务层的在Web层中的桩子。假如我们采用轻量级的SSH技术架构,视图还是JSP,控制器是Struts,而模型就是SpringHibernate这里最难理解的就是模型的概念。我觉得模型是有状态和行为的,那么Struts中的Action调用的Servcie方法就是模型的行为,而返回给JSPDTODO)就是模型的状态。

对于一个页面请求,总要有个地方负责处理和页面跳转的地方,这个地方就是页面控制器。页面控制器有两种,一种是如
Servlet的脚本文件,一种是如JSP的服务器页面。对于Java来说,JSP就是只应该用来显示动态的或静态的信息,而不是用来负责处理RequestRedirect页面,否则Servlet就要失业了。尽管一个Web程序可以全由JSP文件组成,但将控制甚至业务脚本杂乱的穿插在Tag中真的是太糟糕的实践了。

贫血模型:是指领域对象里只有get和set方法,或者包含少量的CRUD方法,所有的业务逻辑都不包含在内而是放在Business Logic层。
优点是系统的层次结构清楚,各层之间单向依赖,Client->(Business Facade)->Business Logic->Data Access(ADO.NET)。当然Business Logic是依赖Domain Object的。似乎现在流行的架构就是这样,当然层次还可以细分。
该模型的缺点是不够面向对象,领域对象只是作为保存状态或者传递状态使用,所以就说只有数据没有行为的对象不是真正的对象。在Business Logic里面处理所有的业务逻辑,在POEAA(企业应用架构模式)一书中被称为Transaction Script模式

充血模型:层次结构和上面的差不多,不过大多业务逻辑和持久化放在Domain Object里面,Business Logic只是简单封装部分业务逻辑以及控制事务、权限等,这样层次结构就变成Client->(Business Facade)->Business Logic->Domain Object->Data Access。
它的优点是面向对象,Business Logic符合单一职责,不像在贫血模型里面那样包含所有的业务逻辑太过沉重。
缺点是如何划分业务逻辑,什么样的逻辑应该放在Domain Object中,什么样的业务逻辑应该放在Business Logic中,这是很含糊的。即使划分好了业务逻辑,由于分散在Business Logic和Domain Object层中,不能更好的分模块开发。熟悉业务逻辑的开发人员需要渗透到Domain Logic中去,而在Domian Logic又包含了持久化,对于开发者来说这十分混乱。 其次,因为Business Logic要控制事务并且为上层提供一个统一的服务调用入口点,它就必须把在Domain Logic里实现的业务逻辑全部重新包装一遍,完全属于重复劳动。

贫血模型是对OO的 非常经典的诠释!数据交给s/g,业务全部交给业务对象来完成。耦合度很低,逻辑清晰,重构空间大!而且在业务逻辑上事务控制的关注点也小!但是也很明 显,业务对象做的事情实在太多了,在领域对象上这个叫做超职责。s/g和业务对象分工虽然明确但工作量截然不同, 也就是说这个对象的职责过于复杂,在一定程度上背离了细力度的OO模型原则。

网友: 对某一个实体信息的管理维护,是分出增加、删除、修改还是只看作一个维护用例?
UMLchina_潘加宇:关键是要找出CRUD背后可能隐藏的业务。

网友: 到底要画哪些图?序列图是必须的吗?
UMLchina_潘加宇:
如果不是面向对象开发,只有用例文档是必须的,然后直接编代码都可以。如果是面向对象开发,需要用类图描述结构,顺序图分配责任。


Martin: 关于这些项目中不写文档的说法是荒诞的。文档和计划当然要做,不过他们不再是过程的一部分,而是过程中的一个任务。在敏捷项目中,会有很多任务,有些任务会包含文档化、项目计划等这些管理团队需要做的规范操作。但它们不再是过程的一部分,这句话的意思是说,它们的秩序不是固定的,我们不是一定要先写文档,也不是一定最后写文档,我们在写文档这个工作变得重要的时候来做它,它会和其它任务一样在日程中进行安排。

Martin: 对.NET开发者而言,在学习敏捷开发的过程中最大的挑战就是测试驱动开发。敏捷开发对单元测试和自动化接受测试的要求很高。TDD现在已经被全行业所接受了,就算你是微软,也是一样的。


那么测试代码是否可以完全取代自然语言形式的设计文档呢?我看还不行,原因有三:
其一,测试代码虽然比源代码容易理解,但它仍然是代码,不是所有人都能理解的;
其二,测试代码的宏观表达能力还是不如自然语言或图表;
其三,很多人习惯看文字而不是看代码,彻底改变人的习惯很难。
所以在TDD开发过程中,比较好的形式是自然语言的文档和测试代码相结合,用自然语言的文档做一个够用的设计就行了,这个设计只要详细到模块关系这一级别就足够了,各个模块的详细设计就由测试代码充当。

Q: 单元测试怎么能反映/代替需求 ?

A: 单元测试未必能直接反映宏观上的需求, 但

  1. 功能测试和集成测试能够反映宏观需求.

  2. 单元测试能够反映系统的其它部分对当前单元的需求.

而从文本的角度, 测试用例的名字就是需求的描述. 换句话说, 你从传统的需求文档中把描述抠出来, 放到测试代码中作为测试用例的名字, 你便拥有了可执行的需求文档


当你试图测试一个单元时, 却发现需要创建大量的其它对象, 而且按照你脑海中的实现, 有些对象是在单元内部创建的, 根本无法在测试环境中假冒它们. 这时候, 你即使只是为了减少测试的难度, 也会逼迫自己思考:

  1. 这个单元是否做了太多的事, 承担了额外的职责, 违反了单一职责原则?

  2. 是否应该把依赖让外界设置进来, 而不是自己在内部创建, 这样测试时就能把依赖设置为假冒的实现?

是的, 单元测试警示你思考一下自己的设计