技术开发 频道

面向对象软件工程方法学实践

【IT168 技术文章】

    两位研究面向对象软件工程的美国学者 (Stave Halladay和Michael Wiebel) 曾这样说:“一般的面向对象编程(OOP)思路不过是一批乌合之众,把灵机一动、随机应变的技巧用于他们绞尽脑汁抽象出来的‘对象’而已。即使是最优秀的 OOP 程序员,他们所能对付的极限也莫过于中等规模的开发项目。倘若程序员经验不足,系统规模又很大,那么采用 OOP 只能把你引入漫无边际的泥沼之中。”

    一方面是几乎没有一位软件工程学者认为 OOP 是完美无缺的,另一方面是 OOP 势如破竹,近乎每一种最新推出的程序开发工具或语言都采用了 OOP 思路;一方面是越来越多的“乌合之众”在毫无章法、随心所欲地处理着“对象”,另一方面是经过近 30 年的积累已经拥有了最大多数用户的结构化软件方法的日渐萎缩……面对这一现实,研究软件工程方法学的专家们纷纷指出:“当前摆在软件开发方法学面前的一个重要课题是:从理论上理解 OOP 具有强大生命力的天然合理性,并完善面向对象软件工程方法学体系。”

    一年来我们通过国内外一些实用系统的开发实践,对面向对象的软件工程方法进行了较为深入的学习和探讨,特别是在北京市公路局计算机系统的一期工程实践中,借鉴国外软件设计经验,较系统地采用了面向对象软件工程方法,受益匪浅。

    一、 是“设计主导”还是“程序主导”

    在一个系统开发过程中是只采用 OOP 还是采用了OOSE(面向对象软件工程)方法,关键看整个开发过程是“设计主导”还是“程序主导”。

    近年来,大量先进程序开发工具进入我国,这对提高软件开发效率无疑具有很大的作用。然而,它们又往往使程序主导型软件开发人员在“以程序代系统”、“以算法代设计”的误区里越陷越深。

    一般的软件开发人员(包括那些只见程序不见系统的程序员)主观上都认为:软件开发不应“系统设计主导”而应“程序算法主导”。但是用下面几个问题考察一下,结果往往相反。

    问题1 在进行软件设计和选择软件开发工具之前,是否进行开发方法学的选择?

    所谓方法学是指组织软件生产过程的一系列方法、技术和规范。方法学是软件开发者长年失败和成功经验的理论性总结,从软件重用的思路来说,方法学重用的价值远非某些程序组件重用可比。

    以北京市公路局系统为例。首先,在系统调查阶段我们了解到:这个系统要分期 (递增式) 开发。由于处于机构改革时期,系统生存期内的用户需求和系统结构变因很多。这表明目标系统应该具有较强的可维护性,即每期开发成果应在后续工程中具有较高的可重用率。其次,一期工程的工作量相当大(最后成果包括 124 个模块、72 类报表、119个数据库表、439 个窗口、912 个数据窗口),而开发者对公路局业务不了解,多为经验不足的大学生,理解需求的能力较低。这表明采用的开发方法学必须能最大限度地减少重复劳动,实现开发过程中的成果共享和重用;必须能支持消除需求理解误差的调整工序,使下游成品阶段的设计变更比较容易进行。

    在开发此系统之前,我们承接了一个国外软件的下游开发任务。由于它采用了面向对象的软件设计,使我们深刻认识到国内外软件开发方法学和技术上的差距,颇受启发。

    参照我们承接的国外软件开发工作量计算方法,即仅下游120个模块 (含报表) 的编码和测试为41人月,那么公路局系统从上游设计开始近200个模块和报表、100多个数据库表的开发工作量至少也应在120人月以上。由于采用了面向对象的软件工程方法,尽管开发人员大多经验不足,但是第一期工程总工时最终仍控制在 80 人月以内,降低成本1/3左右。同时在系统可维护性、重用度及其他功能和性能指标上,均超过了我们以往采用结构化方法开发的系统。

    对停留在程序主导级开发的软件开发人员来说,他们选择 OOP 的原因也往往是被动的。其实,在程序主导开发者的辞典中是找不到“方法学”这一词的,或者把“方法学”与“程序算法”混为一谈。至于把 OOP 看成是 OOSE 的全部就更不足为怪了。

    问题2 对象抽象的出发点是现实世界的问题描述,还是可执行的实例对象?

    在现实世界早期抽象阶段,面向对象方法与其他方法区别并不大,都要从现实世界的问题描述出发,即从用户接口、问题领域的知识和经验出发,构筑现实世界的问题模型,也就是确定目标系统是“做什么的”。面向对象的问题分析模型从3个侧面进行描述,即对象模型 (对象的静态结构)、动态模型(对象相互作用的顺序)和功能模型(数据变换及功能依存关系)。软件工程的抽象原则、层次原则和分割原则同样适用于面向对象方法,即对象抽象与功能抽象原则是一样的,也是从高级到低级、从逻辑到物理,逐级细分。每一级抽象都重复对象建模 (对象识别)→动态建模(事件识别)→功能建模(操作识别)的过程,直到每一个对象实例在物理(程序编码)上全部实现。

    C++、Motif) 都支持OOP,使一些程序级系统开发人员可以很方便地不经过逻辑抽象就直接开发物理对象,在早期阶段意识不到从物理层即实例对象出发进行系统开发的祸患,孰不知正是这种随心所欲的 OOP 不仅无法发挥面向对象方法应有的优越性,而且还会给开发后期带来大量返工作业。

    和以往采用结构化方法一样,我们在系统设计阶段也引入了原型化方法,以便用系统样品即原型与用户对话,求得对需求理解的勾通,避免或减少后期返工。大多OOP工具都为开发原型提供便利,问题在于原型与最终产品间的关系,即原型是逻辑对象还是物理对象的样品。若是后者,那就等同于最终产品。在木已成舟时再让用户评审,若发现问题,要么推倒重来,要么强迫用户削足适履。事实上,我们为设计评审而基于逻辑对象开发的原型,相当部分被用户否决。但由于尚未进行对象实例即物理级开发,而是使用超类对象原型统一模拟对象事件和操作,所以无论是对象模型、动态模型还是功能模型,修改起来都不困难。

    问题3 设计阶段是否先设计超类,是否在实例对象设计开始之前完成超类对象的实现?

    面向对象方法开发出的软件具有较强的可重用性,这种重用包括开发项目内部的重用和外部的重用。重用依存于超类设计,没有超类的对象系统好比“把洗衣机当米缸”,不能物尽其用。超类设计的好与不好,首先看其内部重用率的高低,内部重用率高,必然外部重用率也高。

    由于系统开发工期紧、工作量大,而我们的开发队伍年轻,经验和人力都不足,内部重用率高的超类开发无疑是我们的救星。它可以减少重复劳动,易于统一规格,对复杂问题统一攻关、统一解决,便于统一维护。

    对超类的抽象即实例对象的泛化原则,我们是从下面几个方面考虑的:

    (1)寻找大多数实例对象的共同行为。

    例如“打印报表”、“查询静态代码表”、“录入数据库表数据”等。

    (2)超类的多态性设计要保证使用超类继承关系可以满足各子类的操作要求。

    例如,继承同一个“数据录入”祖先窗口,可以完成不同结构数据库表的数据录入。

    (3)利于信息的隐蔽性,不会破坏数据的完整性,利于将复杂问题简单化。

    例如,对具有复杂关系、结构及相关存取操作的数据库表集的维护。如果不使用一个泛化类将数据结构及其相关操作封装起来,下层程序员要想操作有关库表就必须对库表设计有深入的了解,并且确保程序算法设计不得破坏数据的相关一致性,这将大大增加程序设计和测试的难度,要求程序员有较丰富的经验。而采用这种泛化类 (公用函数、公用存储过程) 后,程序员所要做的只是发“消息”和取“输出信息”了。

    (4)有利于推行开发规范,统一界面风格。

    我们在开发国外软件中受到的最大磨练是:国外对用户界面 (报表、屏幕) 一丝不荀的严格要求。所有屏幕按钮的高、宽、起始位置都用精确到小数点后 3 位的 X、Y 座标进行规定。这样出来的产品使人看上去就有赏心悦目之感。但是如果人人都做界面窗口、按钮的精细调整,工作量势必成倍增长。采用屏幕界面模版超类的继承关系,结合特化处理,问题便可迎刃而解。

    显然,超类的设计和实现必须在程序员普遍进行实例对象开发之前完成。也就是说,OOSE 的上游系统设计人员必须文武 (设计与编程) 双全,能够担负起超类对象的程序实现与测试任务,这与结构化方法的上层系统设计人员基本可以不编程有所不同。同时,超类对象在下游开发过程中必须经常吸收特化过程中的反馈(包括来自用户的反馈),进行相应的调整修改。所以OOSE担任超类对象设计与实现的设计人员很难像结构化方法那样进入编程阶段后就可以稍事轻松,他们往往始终离不开编程现场。

    如果设计阶段不预先设计和开发出超类对象,在同一项目的多数开发者之间没有可以共同继承的祖先对象,甚至在各个开发人员自己的作用范围内都不使用继承关系,那么这不仅不是OOSE,就连称之为OOP都很勉强。

0
相关文章