UML软件工程组织

活用 XP: (三)实践迭代
来源:IBM网站 作者:林星
在了解了分阶段开发软件的基本思路之后,紧接着就需要考虑实施的问题。分阶段开发最难的,并不是在过程的控制上,而是在软件设计能力上。

应用迭代的问题
有一则故事说的是一个人肚子疼,去看医生,医生给他开了眼药,理由是眼神不好,吃错了东西,所以才会肚子疼。软件开发中出现的问题往往不是单纯的问题,头疼医头,脚疼医脚的做法未必适合于软件开发。

应用迭代并不是一件简单的事情,懂得了迭代和增量的概念,并不等于你能够用好它们。为什么这么说呢?很多的软件组织尝试着运用迭代开发,但是结果却不尽人意,于是将问题怪罪在迭代的方法不切实际上。软件工程中有句著名的话-"没有银弹"。迭代和增量也不是什么银弹。要想做好迭代,缺乏优秀的软件设计思想和高明的软件设计师的支持是不行的。在XP中,非常强调各项实践的互为补充。在我看来,迭代能够顺利实行的思路需要重构、测试优先、持续集成等的直接支持。而这些实践,体现了软件设计和软件过程中的关系。

迭代实践出现问题往往是在项目的中期。这个时候,软件的主体已经形成,代码的增长速度也处于一个快速增长的情况。这种状态下的软件开发对变化的需求是最没有抵抗力的,尤其是那些设计本身存在问题的软件。软件开发到这个阶段,代码往往比较混乱,缺乏一条主线或是基础的架构。这时候,需求的变化,或是新增的需求导致的成本直线上升,项目进度立刻变得难以预期,开发人员的士气受到影响。

迭代之外的解决方法
在这个时候,软件组织要做的,并不是在迭代这个问题上深究下去,而是应当从软件设计入手,找到一种能够适应变化的软件设计思路或方法。例如,你是否应该考虑在面向对象领域做一些研究呢?面向对象的思路很注重将变化的内容和不变的内容相区分,以便支持未来的变化和应对不确定性。然后你再来考虑相应的成本。

做好迭代有几个值得注意的地方:

代码设计优化
软件开发的能力并不体现为代码量的多少,而是体现为代码实现的功能,代码的可扩展性、可理解性上。所以对代码进行不断的改进,对设计进行不断的改进(具体的次数根据需要而定),使软件的结构比较稳定,并能够支持变化。这是迭代的一个前提。否则,每一次的迭代都花费大量的精力来对原先的设计进行修改,对代码进行优化,这样的迭代效率是不高的,也可以视为一种浪费。坚持不断改进软件质量的做法其实是将软件的集中维护、改进的成本分摊到整个过程中,这种思路,和全面质量管理的思路是非常类似的。XP中的重构实践有一个修饰词,称为无情。这充分表现了XP的异类,但是应该承认,只有设计和代码的质量上去了,才能够为后续的迭代过程打下一个基础,更何况,XP所处的往往是一个不确定的、变化多端的环境。正是因为这种环境对软件开发有着很大的影响,因此代码质量也被高度的重视。不同的行业,不同的项目,需要根据自己的特征进行调整,但是,只有保证代码的优美性,才能够顺利地达成迭代的目标。

代码设计优化同时必须保持简单的原则,不在一开始进行大量的设计投入。我曾坚信,软件编码之前,严格的软件设计是不可或缺的。但是慢慢的,我发现这种思路未必是正确的。在总结了一些开发经验之后,我发现,很多的时间其实是浪费在了设计上。

在一个软件的设计中,对界面结构有着很强的要求,而Eclipse的设计思路正当其时。因此,我兴奋的将Eclipse的设计思路注入到界面设计上来,在花费了大量的时间进行设计和实现之后,发现并不能很好的满足需要。更为糟糕的是,由于设计的复杂性,导致调试和变更的难度都加大,而团队的其它成员,也表示难以理解这种思路。最后的这个设计废弃了,但是损失已经是造成了,复杂的设计和实现,足足花费了一个星期的开发时间。

重构和审查
除了第一次的迭代,后续的迭代过程都是建立在前一次迭代的基础上。因此,每一次迭代中积累下来的问题最终都会反应在后续的迭代过程中。要想保证迭代顺利的进行,对代码进行重构和审查是少不了的工作。其中最重要的工作莫过于消除重复代码,重复代码是造成代码杂乱的罪魁祸首。消除重复代码的工作可不仅仅只是找出公函这么简单,其间涉及到重构、面向对象设计、设计模式、框架等众多的知识。这些知识的介绍并不是本文的重点,但是我们必须知道,只有严格的控制好代码的质量,软件的质量和软件过程的质量才有保证。

推迟设计决策
精益编程告诉我们,尽可能推迟决策。在一个变化的环境中,早期的决策往往缺乏足够的事实支持和实践证明。即便是再高明的软件设计师,难免会犯错误,这是非常正常的,那么,既然目前的决定是有着很大风险的,那为什么我们还要急于做出决定呢?在看待设计这个问题上,一种比较好的做法是,尽量避免高难度、高浪费的设计,以满足现有的需要作为实现的目标。未来的需求等到确定的时候再进行调整。

推迟决策其实是软件设计的一大能力,为什么我们会推荐使用面向对象技术呢?因为面向对象技术具有很强的推迟决策的能力,先将目前确定的问题纳入面向对象设计,并为未来的不确定性留下扩展。推迟决策并不是一个简单的问题,它需要很强的面向对象的设计思维能力。

设计模式中有很多这方面的例子,其中的装饰模式具有很强的代表性。

在设计刚开始的时候,没有人知道ConcreteComponent最后的发展会是什么样。很明显,这是一个处于不确定环境中的设计,我们唯一能够确定的,只有Component这个类体系一定会拥有Operate这个方法,所以,我们设计了一个接口Component来约束类体系,要求所有的子类都拥有Operate方法。另一个目的是为客户端调用提供了统一的接口,这样,客户端对服务端信息的了解到了最小的程度,只需要知道Operate这个方法,并选择适当的类就可以了。还可以对这个模型做进一步的改进,令耦合程度进一步降低。

在统一了接口之后,我们就可以根据需要来实现现有的功能,我们实现了一个ConcreteComponent类,它实现了Component接口,并实现了核心的功能。如果在未来,需求的变化,要求我们增加额外的行为,我们就使用ConcreteDecorator类来为ConcreteComponent添加新的功能:

 

public class ConcreteDecorator implement Component
{
	private Component component;
	public void Operate()
	{
		//额外的行为
		component.Operate;
	}
}

先找出共通点,然后实现共通点,并把不确定的信息设计为扩展,这就是推迟决策的设计思路。但是,应该指出的是,上面这个例子的设计,仍然有很多的限制,例如,增加的需求(也就是某个ConcreteDecorator)中可能拥有新的接口,例如需要一个AnotherOperate方法,这时候,原先的扩展性设计就又变得难以满足需要了。在软件设计中,针对接口设计的灵活性和扩展性虽然比以往的设计增强的许多,但它并不是万能的,而且取决于设计师对需求的理解能力和设计水平。此外,推迟设计决策要求我们学习抽象的思维,识别和区分软件中变化和不变的部分。

注重接口,而不是注重实现
Martin Fowler把软件设计分为三个层面:概念(conceptual)层面、规约(Specification)层面、实现(Implementation)层面。软件的设计应该尽可能地站在概念、规约层面上进行,而不是过分关注实现层面。之所以有时候我们发现在迭代的过程中,软件难以承受这种变化,那么,很大的可能是规约层面和实现层面出了问题。我们在前面一节讨论重构和审查的时候说,消除重复代码是一项复杂的工作,针对规约设计就是其中最有效,但也是最难的一种方法。

我们可以把规约层面想象为软件的接口或是抽象类,或是具体类的公有方法,而把实现层面想象为实现类、实现细节。那么,我们的原则应该尽可能设计稳定的规约层面,并为客户(可能是真正的客户,大部分情况下是使用你的代码的客户程序员)提供一个优秀的、简单的界面(接口)。社会发展到现在的水平,任何一个人都不会花费过多的时间来研究你的代码,如果你的代码不能够为他人提供便利性,那么最后被淘汰的一定就是你的代码。Java语言的成功,很大程度上就在于他在保证其强大功能的同时,还提供了一个简单、易用、清晰的规约界面。

在软件设计中,重视规约层面的设计是很普遍的。为什么我们提倡三层架构的软件设计?最重要的是因为他为软件结构合理性贡献巨大,远远超过了他的其它价值。在现代的软件设计中,数据库、界面、业务建模其实是三种差异较大的技术,这就导致了三者的变化度是不同的。根据区分不同变化度的原则,我们知道,必须对三种技术进行区分。而这正是三层架构的主要思路。从这个思路扩展出去,我们还可以根据变化度的需要,将三层架构演变为四层架构、甚至多层架构。而多个层次之间,正是通过优秀的规约界面来达到最松散的耦合的。

在精益编程中,为了避免浪费,要求每位程序员提高代码的规约层面的稳定性是非常有必要的。一个系统中,设计优良的规约界面能够拥有比较好的抗变化能力,能够较好的适应迭代过程。

回归
版本2的软件出现了版本1中不存在的行为,称为回归。回归是软件开发中的主要问题。在对现有功能修改的同时影响原有的行为,这是造成bug的主要原因。在迭代的过程中,必须避免回归行为的出现。而避免回归问题的主要解决方法是构建自动化的测试,实现回归测试。

成功构建回归测试的关键仍然在于是否能够设计出优秀的规约界面,并针对规约界面进行测试。这样,不但设计具有抗变化性,测试同样具有抗变化性。而唯一可能改变的就只有实现了。在回归测试的帮助下,代码的变化是不足为惧的。我们把有关测试的详细讨论放在测试一节中。

组织规则
在后续的章节中,我们会详细的讨论XP中的一项非常有特点的组织规则-结对编程。这里我们需要知道,不同的团队有着不同的组织,其迭代过程也需要应用不同的组织规则。例如,组织的规模,小规模的组织可以应用更快的迭代周期,如一周,在一个迭代周期中,团队可以集中力量来开发一个需求,强调重构和测试,避免过多的前期设计。对于大的组织来说,可以考虑迭代周期更长一些,更注重前期设计,并将开发人员和测试人员的迭代周期交错开来。团队的组织构成也是影响迭代过程的主要原因。团队是否都是由相同水平的人构成,每个人的专长是否能够互补,团队是否存在沟通问题。

作者简介
林星,辰讯软件工作室项目管理组资深项目经理,有多年项目实施经验。辰讯软件工作室致力于先进软件思想、软件技术的应用,主要的研究方向在于软件过程思想、Linux集群技术、OO技术和软件工厂模式。您可以通过电子邮件 iamlinx@21cn.com 和他联系。

 

 

版权所有:UML软件工程组织