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

  两位研究面向对象软件工程的美国学者 (Stave Halladay和Michael Wiebel) 曾这样说:“通常的面向对象编程(OOP)思路不过是一批乌合之众,把灵机一动、随机应变的技巧用于他们绞尽脑汁抽象出来的‘对象’而已。即便是最优秀的 OOP 程序员,他们所能对付的极限也莫过于中等规模的开发项目。假若程序员经验不足,系统规模又很大,那么采用 OOP 只能把你引入漫无边际的泥沼之中。”   一方面是几乎没有一位软件工程学者认为 OOP 是天衣无缝的,另外一方面是 OOP 势如破竹,近乎每一种最新推出的程序开发工具或语言都采用了 OOP 思路;一方面是愈来愈多的“乌合之众”在毫无章法、为所欲为地处理着“对象”,另外一方面是通过近 30 年的积累已经拥有了最大多数用户的结构化软件方法的日渐萎缩……面对这一现实,研究软件工程方法学的专家们纷纷指出:“当前摆在软件开发方法学面前的一个重要课题是:从理论上理解 OOP 具备强大生命力的自然合理性,并完善面向对象软件工程方法学体系。”
  一年来咱们经过国内外一些实用系统的开发实践,对面向对象的软件工程方法进行了较为深刻的学习和探讨,特别是在北京市公路局计算机系统的一期工程实践中,借鉴国外软件设计经验,较系统地采用了面向对象软件工程方法,受益不浅。
1、 是“设计主导”仍是“程序主导”
  在一个系统开发过程当中是只采用 OOP 仍是采用了OOSE(面向对象软件工程)方法,关键看整个开发过程是“设计主导”仍是“程序主导”。
  近年来,大量先进程序开发工具进入我国,这对提升软件开发效率无疑具备很大的做用。然而,它们又每每使程序主导型软件开发人员在“以程序代系统”、“以算法代设计”的误区里越陷越深。
  通常的软件开发人员(包括那些只见程序不见系统的程序员)主观上都认为:软件开发不该“系统设计主导”而应“程序算法主导”。可是用下面几个问题考察一下,结果每每相反。
问题1 在进行软件设计和选择软件开发工具以前,是否进行开发方法学的选择?
  所谓方法学是指组织软件生产过程的一系列方法、技术和规范。方法学是软件开发者终年失败和成功经验的理论性总结,从软件重用的思路来讲,方法学重用的价值远非某些程序组件重用可比。
  以北京市公路局系统为例。首先,在系统调查阶段咱们了解到:这个系统要分期 (递增式) 开发。因为处于机构改革时期,系统生存期内的用户需求和系统结构变因不少。这代表目标系统应该具备较强的可维护性,即每期开发成果应在后续工程中具备较高的可重用率。其次,一期工程的工做量至关大(最后成果包括 124 个模块、72 类报表、119个数据库表、439 个窗口、912 个数据窗口),而开发者对公路局业务不了解,多为经验不足的大学生,理解需求的能力较低。这代表采用的开发方法学必须能最大限度地减小重复劳动,实现开发过程当中的成果共享和重用;必须能支持消除需求理解偏差的调整工序,使下游成品阶段的设计变动比较容易进行。
  在开发此系统以前,咱们承接了一个国外软件的下游开发任务。因为它采用了面向对象的软件设计,使咱们深入认识到国内外软件开发方法学和技术上的差距,颇受启发。
  参照咱们承接的国外软件开发工做量计算方法,即仅下游120个模块 (含报表) 的编码和测试为41人月,那么公路局系统从上游设计开始近200个模块和报表、100多个数据库表的开发工做量至少也应在120人月以上。因为采用了面向对象的软件工程方法,尽管开发人员大多经验不足,可是第一期工程总工时最终仍控制在 80 人月之内,下降成本1/3左右。同时在系统可维护性、重用度及其余功能和性能指标上,均超过了咱们以往采用结构化方法开发的系统。
  对停留在程序主导级开发的软件开发人员来讲,他们选择 OOP 的缘由也每每是被动的。其实,在程序主导开发者的辞典中是找不到“方法学”这一词的,或者把“方法学”与“程序算法”混为一谈。至于把 OOP 当作是 OOSE 的所有就更不足为怪了。
问题2 对象抽象的出发点是现实世界的问题描述,仍是可执行的实例对象?
  在现实世界早期抽象阶段,面向对象方法与其余方法区别并不大,都要从现实世界的问题描述出发,即从用户接口、问题领域的知识和经验出发,构筑现实世界的问题模型,也就是肯定目标系统是“作什么的”。面向对象的问题分析模型从3个侧面进行描述,即对象模型 (对象的静态结构)、动态模型(对象相互做用的顺序)和功能模型(数据变换及功能依存关系)。软件工程的抽象原则、层次原则和分割原则一样适用于面向对象方法,即对象抽象与功能抽象原则是同样的,也是从高级到低级、从逻辑到物理,逐级细分。每一级抽象都重复对象建模 (对象识别)→动态建模(事件识别)→功能建模(操做识别)的过程,直到每个对象实例在物理(程序编码)上所有实现。
  对象抽象是从逻辑级仍是物理级出发,与开发前是否进行方法学选择同样,也是区分OOSE 与 OOP 的试金石。因为许多工具或语言(如PB、C++、Motif) 都支持OOP,使一些程序级系统开发人员能够很方便地不通过逻辑抽象就直接开发物理对象,在早期阶段意识不到从物理层即实例对象出发进行系统开发的祸患,孰不知正是这种为所欲为的 OOP 不只没法发挥面向对象方法应有的优越性,并且还会给开发后期带来大量返工做业。
  和以往采用结构化方法同样,咱们在系统设计阶段也引入了原型化方法,以便用系统样品即原型与用户对话,求得对需求理解的勾通,避免或减小后期返工。大多OOP工具都为开发原型提供便利,问题在于原型与最终产品间的关系,即原型是逻辑对象仍是物理对象的样品。如果后者,那就等同于最终产品。在木已成舟时再让用户评审,若发现问题,要么推倒重来,要么强迫用户削足适履。事实上,咱们为设计评审而基于逻辑对象开发的原型,至关部分被用户否决。但因为还没有进行对象实例即物理级开发,而是使用超类对象原型统一模拟对象事件和操做,因此不管是对象模型、动态模型仍是功能模型,修改起来都不困难。
问题3 设计阶段是否先设计超类,是否在实例对象设计开始以前完成超类对象的实现?
  面向对象方法开发出的软件具备较强的可重用性,这种重用包括开发项目内部的重用和外部的重用。重用依存于超类设计,没有超类的对象系统比如“把洗衣机当米缸”,不能物尽其用。超类设计的好与很差,首先看其内部重用率的高低,内部重用率高,必然外部重用率也高。
  因为系统开发工期紧、工做量大,而咱们的开发队伍年轻,经验和人力都不足,内部重用率高的超类开发无疑是咱们的救星。它能够减小重复劳动,易于统一规格,对复杂问题统一攻关、统一解决,便于统一维护。
  对超类的抽象即实例对象的泛化原则,咱们是从下面几个方面考虑的:
  (1)寻找大多数实例对象的共同行为。
    例如“打印报表”、“查询静态代码表”、“录入数据库表数据”等。
  (2)超类的多态性设计要保证使用超类继承关系能够知足各子类的操做要求。
    例如,继承同一个“数据录入”祖先窗口,能够完成不一样结构数据库表的数据录入。
  (3)利于信息的隐蔽性,不会破坏数据的完整性,利于将复杂问题简单化。
    例如,对具备复杂关系、结构及相关存取操做的数据库表集的维护。若是不使用一个泛化类将数据结构及其相关操做封装起来,下层程序员要想操做有关库表就必须对库表设计有深刻的了解,而且确保程序算法设计不得破坏数据的相关一致性,这将大大增长程序设计和测试的难度,要求程序员有较丰富的经验。而采用这种泛化类 (公用函数、公用存储过程) 后,程序员所要作的只是发“消息”和取“输出信息”了。
  (4)有利于推行开发规范,统一界面风格。
    咱们在开发国外软件中受到的最大磨练是:国外对用户界面 (报表、屏幕) 一丝不荀的严格要求。全部屏幕按钮的高、宽、起始位置都用精确到小数点后 3 位的 X、Y 座标进行规定。这样出来的产品令人看上去就有赏心悦目之感。可是若是人人都作界面窗口、按钮的精细调整,工做量势必成倍增加。采用屏幕界面模版超类的继承关系,结合特化处理,问题即可迎刃而解。
  显然,超类的设计和实现必须在程序员广泛进行实例对象开发以前完成。也就是说,OOSE 的上游系统设计人员必须文武 (设计与编程) 双全,可以担负起超类对象的程序实现与测试任务,这与结构化方法的上层系统设计人员基本能够不编程有所不一样。同时,超类对象在下游开发过程当中必须常常吸取特化过程当中的反馈(包括来自用户的反馈),进行相应的调整修改。因此OOSE担任超类对象设计与实现的设计人员很难像结构化方法那样进入编程阶段后就能够稍事轻松,他们每每始终离不开编程现场。
  若是设计阶段不预先设计和开发出超类对象,在同一项目的多数开发者之间没有能够共同继承的祖先对象,甚至在各个开发人员本身的做用范围内都不使用继承关系,那么这不只不是OOSE,就连称之为OOP都很勉强。
问题4 如何处理对象模型面向对象关系数据库模式的映射?
  面向对象的数据库设计方法能够用于各类数据库,如层次型、网络型、关系型,固然也包括面向对象型。OOSE 中的数据库设计无疑必须采用面向对象的数据库设计方法。
  数据库设计也称数据库模式,基本上由3个层次的模式构成:从特定DB应用角度来看待DB设计的外部模式;从组织或企业角度出发进行的DB设计即概念模式;处理对应特定 DBMS 特征与局限性的DB设计即内部模式。具体而言,内部模式是数据库的SQL定义,逻辑模式是表集合的逻辑定义,外部模式是从特定应用角度看的局部DB。外部模式与逻辑模式之间的接口是视图、存储过程或其余驻在服务器端的DB处理程序。
  若是在抽象出的对象模型中,各个应用分别是一个或多个超类对象的子对象,那么,选择适当细分层次的对象模型将其映射到概念模型,是数据库库表对象设计的关键。外部模式与概念模式之间的接口越少、越简单越好,这样的程序设计简单,数据库和程序都易于维护。也就是说,局部化是个重要的设计原则。
  OOP可能是数据库的后端处理,是基于既存数据库的。所以不管是否进行过问题世界的对象建模,以及是否将对象模型合理地映射到数据库逻辑模式 (面向对象数据库设计),OOP 均可以工做。
问题5 编程时是否先调查有无可重用 (继承) 对象,是否参与下层对象对上层对象、超类对象的反馈?
  埋头于本身分担的程序对结构化方法或许是必须的,但在面向对象方法中担任程序设计的开发人员,应该先去调查对象数据辞典中有无其余开发人员已经完成、本身稍加特化就可重用的对象。从整体上说,对象的共享、重用应该由上层设计人员统一管理,以便保证对象风格的一致性,避免冲突。可是,对象的独立性、封装性和多态性都很便于重用,这是结构化系统所不能比拟的,而重用是软件开发方法学的最重要思想之一。上层设计人员每每不可能面面俱到,懂得软件设计理论的开发人员,即便只开发下层程序也应采用最省力、最有效率的编程方法,即大量使用重用对象。
  在继承超类对象和重用他人对象时,若发现有设计不合理的地方,应该及时反映给对象开发的承担者。
  对上层设计人员来讲,一方面应该鼓励程序实现人员重用既存对象,另外一方面应经过开发人员共享对象数据辞典,使个别的对象重用可以当即反映到总体对象模型中,以保证设计变动时的一致性。
2、面向对象方法与结构化方法比较
  分析是问题抽象 (作什么),设计是问题求解 (怎么作),实现是问题的解 (结果)。任何方法学对客观世界的抽象和求解过程都是如此。在问题抽象阶段,结构化方法面向过程,按照数据变换的过程寻找问题的结点,对问题进行分解。所以,与面向对象方法强调的对象模型不一样,描述数据变换的功能模型是结构化方法的重点。若是问题世界的功能比数据更复杂或者更重要,那么结构化方法仍然应是首选的方法学。若是数据结构复杂且变换并很少,那么如以过程主导分析和设计,一旦有系统变动就会给下游开发带来极大混乱。
  因为对过程的理解不一样,面向过程的功能细分所分割出的功能模块有时会因人而异。而面向对象的对象细分,从同一问题领域的对象出发,不一样人得出相同结论的比率较高。
  在设计上,结构化方法学产生自顶向下、结构清晰的系统结构。每一个模块有可能保持较强的独立性,但它每每与数据库结构相独立,功能模块与数据库逻辑模式间没有映射关系,程序与数据结构很难封装在一块儿。若是数据结构复杂,模块独立性很难保证。面向对象方法抽象的系统结构每每并不比结构化方法产生的系统结构简单,但它能映射到数据库结构中,很容易实现程序与数据结构的封装。
  在软件工程基本原则中有一条“形式化原则”,即对问题世界的抽象结论应该以形式化语言 (图形语言、伪码语言等) 表述出来。结构化方法能够用数据流图、系统结构图、数据辞典、状态转移图、实体关系图来进行系统逻辑模型的描述;而面向对象方法可使用对象模型图、数据辞典、动态模型图、功能模型图。其中对象模型图近似系统结构图与实体关系图的结合,动态模型图相似状态迁移图,功能模型图相似数据流图。
  公路局系统有 100 多个数据库表,但数据的加工 (变换) 很单纯,若是当初选择结构化方法学,状况会怎么样?
  在问题抽象的最初阶段不会有太大差别。因为数据变换少,能够把对象和对象的操做当作一一对应,即最初问题描述的对象模型与功能模型基本一致。以其中计划管理处子系统为例,对象是计划管理员、规划管理员、概预算管理员、统计管理员,功能 (操做) 是计划、规划、概预算、统计。
  问题存在于下层抽象里。
  首先,许多公共超类对象设计与结构化方法相悖,由于它破坏了过程的连续性及系统结构的逻辑层次性,把一些下层模块及在过程分析中没有语义的对象,放在系统结构的上层。所以若是采用结构化方法,须将继承关系改成下层模块调用关系。可是事实上,祖先对象的一些状态 (属性值) 是从主控模块直接获得指示而肯定的;从控制角度说,它的确处于系统的上层地位。若是采用结构化方法,结果将是要么把系统结构变成网络状,失去结构化特征,要么放弃这种统一完成重复性劳动的设计方案。
  其次,应用对象模型向数据库概念模式的映射设计也是该系统采用面向对象方法的一个标志。若是使用结构化方法,数据库模式可能映射客观世界的数据结构。因为公路、养路单位、管理单位、路况、桥梁、隧道及道路上的绿化状况等各实体间客观存在着复杂的多重关系,其结果可能定义出一个像蜘蛛网似的关系库结构,于是大大加剧了数据库前端应用编程和数据库维护的负担。
  总之,该系统若使用结构化方法,系统结构和数据库结构均可能成为网状结构,且互相无关。而目前采用的面向对象方法,系统结构和数据库结构都是多重继承结构,相互存在映射关系。显然前者较后者复杂性高、可维护性差、内部重用难度大、重用率低。
  其实,不管是用什么方法学开发软件,交给用户的都应该是知足用户当前需求的软件。用户在短时间内不会发现开发者使用先进方法学给他们带来的益处,却是开发者自己因为大大减轻了开发负担而最早受益。可是随着时间的推移,得到最大收益的仍是用户,由于软件的长期质量(包括维护成本低和生存周期长)给用户带来的好处才是根本的。
3、方法学是思路不是定律
  对于方法学,咱们是这样理解的:
  (1)方法学的目的是:使后人分享前人的成功,避开前人的失败,把注意力集中在还没有开拓领域的创造性劳动上。因此方法学与开发人员的创造性是毫不冲突的。它既不能像法律那样靠权威来界定是非边界,也不能像定律那样经过证实和推理给出广泛结论。若是必定要作比喻的话,它比如人的世界观。
  (2)没有放之四海而皆准的方法学,任何方法学都有其局限性,因此软件开发人员大可没必要拘泥于某种特定的方法学。
  例如,面向对象方法的对象模型图,这种形式化语言远不如结构化方法的结构图和数据流图简单明了,假若把公路局系统所有用对象模型图表述出来,至少也要几十页。因为最上层功能模型与对象模型是一致的,因此咱们采用的是结构化方法的系统结构图。
  (3)事实代表,由 OOP 带动的 OOSE 方法确实比结构化方法更能天然地抽象现实世界,并且一些 OOP 工具确实已至关成熟。相反,结构化方法及开放平台下的结构化程序开发工具,虽然不能说止步不前,但其近年来的进步是有限的。
  (4)根据咱们的体会,对实践 OOSE 有如下一些建议:
    1 最好在选定方法学后,对全体开发人员进行一次关于面向对象方法学的培训
    2 因为有超类对象的提早开发工做,OOSE 的上游设计工做量比结构化方法的上游工做负担重,时间和人力应该更充足一些。不然到下游开发后再追加或屡次修改变动超类对象,容易形成混乱和无效劳动。
    3 因为系统越大对象类越多,为了便于内部重用和共享,应该创建电子化的对象数据辞典,以便对对象进行统一归类管理。
    4 应该有严格的命名规则,若是可能,应将命名规则集成到数据辞典中。
    5 下层开发铺开后,若是发现应该对某些实例对象泛化成新的超类对象,必须尽快进行新超类追加的设计,变动越快越好。
    6 子对象继承超类对象后,发现超类设计的缺陷是常有的事。开发队伍内部应有很畅通的反馈渠道,使超类获得及时的修正。子对象切不可轻易将超类对象封杀掉,使系统失去统一控制。听从系统设计中定义的继承关系进行实例对象开发应该成为全体开发人员的理念。
    7 面向对象设计的好处越到后来越显著,特别是在系统维护和扩充方面。前端

相关文章
相关标签/搜索