《专栏声音》面向对象简史 (附: dotNET Architect们应

    技术2022-05-11  31

    <script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script> <script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

    《专栏声音》面向对象简史 (: dotNET Architect们应该思考的问题)

     

    小气的神 2002.05.06

     

    现在如果还为面向对象而争论得面红耳赤似乎有些老式了,正如对于是否要先画流程图再写程序的争论一样,我们早已心明释然。我一直以为自己是一个比较清淡的“面向对象”进化派,更钟情和热衷于“面向组件”多一些。一旦喜欢上由许多组件(Component)构成的分布式环境似乎就很难把目光重新聚集到组件内部的对象身上了,组件间的相互关系和作用足够构成一个非常有趣的信息环和链,看着信息在这个环网中穿梭流动更容易让人心动。当看到COM+中上百个金色小球不停地转动,谁又说这番景象不让人很沉迷呢。

     

    不过,dotNET技术带来了新的特性,使得我们需要重新看待有关我们的程序架构和组成,特别是更加细致和微观的方面,比如上面说的组件内部构造。XMLSOAPWSDLWeb Services解决了组件间通讯的接口问题,不同环境中的应用和组件可以通过一个共同的与平台,与语言工具无关的协议进行通讯和交换数据。但是每个组件内部呢?它的构造和精巧程度是否会影响我们的应用和开发呢?

     

    一直以来我们习惯倾向于使用COM+的组件可能源于一种实用的观点,它并不需要太多面向对象的观点和约束,按功能将一组相关的实现函数收集在一起并将其封装成一个对象。当然,对于“面向对象“的狂热派来说这是不可思议的,他们认为这不是真正的面向对象的设计和实现,在感觉上你是冒用了面向对象,内心里他们认为这根本不是面向对象。当然这些感觉不是我们要解决的,我们要面对的是分层的重用、有效的抽象和高效的耦合。

     

    在面向对象的分析和设计中,抽象的质量是通过两个指标来衡量的,一个是内聚性,即如果抽象能够简洁的代表一种清晰的概念(客户、发票、订单),那么抽象是内聚的。一个类的所有方法必须直接和概念的表示相关。另一个是耦合度。即与相关联的抽象的耦合程度。如果耦合度高,那么重用性就低,因为该抽象与系统中的其它模块相关以致于很难单独维护。结论是:较弱的抽象不能被重用,高耦合丧失了重用性。这也是我们经常遇到的,我们的组件很容易使用-高耦合,任意时刻看一下API手册就可以用CreateObjectCoCreateInstance创建一个组件,然后调用它的方法就获得某种功能。但往往组件也没有重用性,组件是基于功能抽象出来的接口集合对象,这种抽象的内聚性很低,受尽可能少的状态的组件思想鼓励,你的类中几乎没有成员变量,而方法的实现突出完成某种功能而不是集中体现某个清晰的概念,很可能看见你的组件中有新增一张发票、删除一个客户记录,修改一张订单的功能函数混合在一起,有何不可它们可以设置相同的事务属性,如果分类的好,客户端只有创建一个组件就可以完成相关的一系列业务操作。这也许就是我说的实用性了。但组件内部没有或很少有对象的观点或概念,所以几乎没有重用性,往往掌握了一个框架之后你只需拷贝和修改代码,事实上你所有的精力可以放在业务细节上了,但不同的业务细节可能需要你不断地产生新的组件。

     

    当然必须承认,在COM+项目中使用面向对象的分析和设计原则是一个颇具难度和棘手的问题。某种开发系统会在不同方面影响你的项目结构,造成包括上述的一些问题。比如VB就是一个典型的例子,由于简化的目标,VB设计成统一使用缺省的接口来区分coclass,这使得许多开发人员不知不觉中将对象的所有行为放在这个主接口中。这是否是基于接口的开发呢?是,但也不是。主接口中放置的是对象特有的行为的一些集合,该集合是独特的,集中反映其抽象的。把对共享的行为定义在一个独立的接口中,并把对这些接口的共享实现放在独立的模块中,这样软件重用成为可能。接口实现可重用的关键是使接口的实现与某个对象的实现非耦合。往往多是一些VB程序员会提这样的问题,dotNET的编程是基于接口的编程,怎么体现呢?其实接口不仅在dotNET中,VB时期也存在,接口是从你的设计中体现出来。还有关于COM继承(implementation inheritance)的问题,为了保证COM的有效性和组件特性,COM的制定者不支持也不打算支持继承,他们认为继承或多继承(MI)是构架应用程序的好工具,但不适合于为面向组件的系统对象模型定义结构。事实上在COM世界中你看到的更多的是包容和聚合的方式来实现重用。所以,某种程度上我们的项目结构受到了一定影响,当然也包括其中的开发人员。

     

    dotNET框架带来了许多改善,比如接口、对象、属性、继承等等,更多的支持面向对象的分析和设计实现,甚至有些是全新的。所以我们有机会可以重新考虑在我们的dotNET应用设计和实现中使用面向对象的设计和分析,带来比以往更多的可重用性。关键在于我们是否可以看到这一点并对应的进行转变,往往我们忽略了这些。不能说Mary Kirtland在红宝书中提出的三层或多层组件构架模型已经过时了,但至少应该受到挑战,把这种模型硬搬到dotNET平台下可能会有些不合时宜(当然不是不行,性能会比以前高许多),但对于其上的设计者和开发人员可能是一个不小讽刺:你们在等什么呢?

     

    我的观点是:先逐步的使用面向对象的分析和设计,使你的组件内部具有抽象性,提高可重用性的可能性。调整原来的接口设计方案,进行抽象和早的接口隔离。最通俗的意义是从此我们的表现层调用一个business层的对象的方法后不再返回ADO的记录集或Dataset,而是抽象后的对象,表现层通过对象的属性和方法来操作。一个订单对象可能是一个组件也可能是组件中的一个对象,但这个对象中所有的操作是关于现实中的一张订单。当然这里有个量度的问题,即可能遇到多一点面向对象呢还是少一点,我的组件比你的组件多一点面向对象的争论。这里的标准是取决于你的实际应用,面向对象所带来的益处必须为项目中的实际应用服务,要能够解决问题。如果不能,那么你可以保持使用过去的方法,但如果两者都可以,那么你应从可重用性的角度出发,抽象它。我的一位朋友曾经对我说:抽象为了编程简单;面向对象,不是为了可重用吗?有人说出生在70年代的许多人,不喜欢或从不带手表,所以也显得格外怀旧;我想我的朋友和我都属于这一类了。

     

    第二步是借助设计模式(Design Patterns)。当我们使用面向对象分析技术整理业务需求,并在开发生命周期的早期进行了对象的抽象、验证和设计。那么从分析到设计、从设计到实现的过程就变得清晰了。但如何灵活应用和组合上述的抽象,最好和代价最小的办法是借助设计模式,它是面向对象的集大成者。有两个很重要的设计原则体现在设计模式中:基于接口而不是基于实现进行编程和优先使用对象组合而不是类继承。这两个原则也贯彻在整个dotNET的平台的设计和开发中,从设计到dotNET的实现是可以同步和一一对应的。另外设计模式是在一定场景下,针对何种问题的一个解决方案(solution),它强调最后的作用力,效果(consequences)如何。必须承认J2EE应用到企业级的系统开发过程中,已经总结出不少对应于业务模式的设计模式,这对于技术平台本身意义重大,使其充满魅力和生命力。而dotNET平台下的开发人员才刚刚开始,不过我相信一旦借助了设计模式,你也会感到dotNET平台的活力,它更灵活也让你有更多的选择。而这些都是设计和改进应用构架必须的:其一是借助模式的可重用思想体现和调整你的设计;其二是在运用的过程中,根据你具体的业务模式运用和组合各种模式产生对应某种业务的特定核心设计模式(说核心系统架构会更好一些,这些架构可能表现出一种设计模式也可能是几种设计模式的混合或是一种你创造的新模式,它主要针对你具体的业务模型、环境、流程和平台的一种设计模式或一种架构体系,也就是说在后一个层次上它可能是特殊的,更多针对业务模式本身而不单单强调设计模式)。

     

    第三步,和你的团队一起进行这种转变。象上一篇《专栏声音》中提到的那样,你会发现你的团队中有两类人,一类是领域专家,他们的兴趣是为某个领域提供解决方案,往往他“面对组件”多一些,因为受实用主义驱使,他被要求一定时间内从不同领域中找到合适和有效的解决方案;另一类是技术专家,他们关心解决方案如何实现,往往他“面向对象”多一些,因为他们的乐趣来自每个类的抽象和每个接口之间的尽善尽美。新技术下,领域专家应当主动注意到这种变化并积极的做成相应的改变,更多的协同技术专家一起完成系统的分析和架构。而技术专家会充分发挥他的优势以提高整个应用的可重用性和稳定性。(感觉中一般领域专家多是Project manager System design,技术专家多是Program managerTeam leader,有趣的是他们有另外一个相同的角色:Senior Software Developer,所以都会在一起编程完成实现。不过这些并非属实,题外话haha)。

     

    毫无疑问,几乎所有的dotNET开发和设计者都是战战兢兢的走上这个看似熟悉明朗但又充满灰暗灯光的舞台,准备开始一场现在与未来之间的华尔兹,在聚光灯没有打开之前,我们需要抓住一些我们熟悉的道具,看清舞台的环境。不然,当灯光亮起,音乐响起时我们可能已经又一次的被这个旋转舞台抛到了半空中。

     

    好了,我想我是跑题了,原本是想摘录下面这些有关面向对象的一些信息,然后有感而发:我们应当在使用dotNET技术时主动的运用和实现面向对象的思想。而现在看来,“离谱”了一些:) 但是,请原谅我。

     


    面向对象中的九个非常重要的软件概念:

    封装、信息实现/隐藏、状态保持、对象标识、消息、类、继承、多态性、一般性。

     

    面向对象的起源:

    与人类发明史上的许多创举不同,面向对象不是瞬间出现的。面向对象不是某个人在浴缸中的突发其想,而是许多人历经多年的研究积累得产物。面向对象概念就像几条支流通过历史的变迁而汇集到一起,最后形成面向对象的河流。

    下面列举了10位(按年代)在理论研究方面和在工程实际中,对面向对象做出突出贡献的人士,(如有遗漏请谅解)

    l          Larry Constantine

    谈到任何软件范畴的贡献者都会提到Larry Constantine,因此就从资深的Larry Constantine说起。虽然20世纪60年代,Constantine并没有在“面向对象”的旗号下做任何事情,但他却致力于研究软件设计的基本准则。实际上,他是最先提出软件在编程之前进行设计的几个人之一。Constantine的许多著名观点(如耦合和内聚)一直沿用到现今的面向对象领域。

    [ Constantine ,1968] Constantine , L.L. “Control of sequence and Parallelism in Modular programs,” AFIPS Proceedings of the 1968 Speing Joint Computer Conference Vol.32 (1968), p409

    l          .-J. Dahl K.Nygarrd

    DahlNygaard引入的几个概念现在已成为面向对象的组成部分。类的概念就是一个最好的例子,这个概念首次出现在Simula语言

    [Dahl and Nygaard,1966] Dahl, O.-J., and K.Nygaard. “SIMULA-An Algol-Based Simulation Language.” Communications of the ACM, Vol.9, No.9 (1996), p32~42

    l          Alan Kay, Adele Goldberg 等人

    Kay,Goldberg及其同事经过几年的研究,于1970年左右在Xerox公司的Palo Alto研究中心设计出Smalltalk语言。这一研究成果提出了许多现在成为面向对象核心的概念(如消息和继承)。许多人至今仍然认为Smalltalk语言和环境是面向对象完美的实现。

    [Kay, 1969] Kay, A The Reactive Engine. University of Utah , Department of Computer Science, August,1969

    [Goldberg and Robson,1989] Goldberg, A., and D. Robson.Smalltalk-80: The Language. Reading , Mass. :Addison-Wesley, 1989

    l          Edsger Dijkstra

    Dijkstra的“软件正确性的理念(Conscience of Software Correctness)”,使人们几十年来一直耿耿于怀。在Dijkstra的早期研究中,提出了用抽象层构造软件的观点,在两个相继的层之间用严格的语义区分。这实际上是一种封装的形式,也是面向对象的主要概念之一。

    l          Barbara Liskov

    20世纪70年代,Liskov使抽象数据结构(ADT)的理论和实现有了重大的进展,奠定了面向对象的基础。Liskov的最著名的研究成果就是CLU语言,支持隐藏内部数据表示方法。

    [Liskov et al, 1981] liskov, B., etal. CLU Reference Manual. New York : Springer-verlag, 1981

    l          David Parnas

    在具有划时代意义的论文中,Parnas提出了模块软件构造原则。尽管面向对象的构造优于传统的过程模块,但Parnas的信息隐藏的许多基本思想仍可以应用到面向对象的系统中。

    l          Jean Ichbian 等人

    Ichbiah与研究小组开发了“Green”编程语言,是一种被美国国防部所采用的Ada语言(现在称为Ada-83)。Ada83中的两个概念(一般性和包)也是面向对象中非常重要的内容。这个语言的最新版本Ada95更为全面的支持面向对象。

    l          Bjarne Stroustrup

    C++语言有一个有趣的家谱。以前由Martin Richards开发了一种BCPL语言。由此产生B语言,BCPL的缩写。由B语言产生C语言,经过Stroustrup的研究由C语言产生了面向对象的C++语言。下面引用一段Stroustrup在“C++的产生”中的一段话:

    C++主要是为编程人员而设计的,使其编程时不一定必须使用汇编、C或各种现有的高级语言。其主要目的让每个编程人员可以更容易更愉快地写出好的程序。历来没有有关C++设计的论文;设计、文档以及实现都是同时进行的。

    由于C++的面向对象是从早期非面向对象并且十分低级的语言移植而来的,因而它的语法并不十分清晰。然而,尽管有Java语言出现,C++仍然是最广泛使用的面向对象语言。由于C++的前身是C,因此在许多机器和操作系统平台上具有可移植性,从而极大地推动了面向对象语言的流行。从这个意义上说,Stroustrup对该领域的贡献是巨大的。

    l          Bertrand Meyer

    Meyer的贡献是将最佳的计算机科学思想与最佳的面向对象思想融合在起来。其结果是产生了一个称为Eiffel的语言和环境。Eiffel在软件界确实是珍品,因为它对理论、软件工程以及希望优化代码的人们都具有吸引力。无论你的企业选择那种面向对象语言,如果希望成为真正的面向对象专家,就应该学习Eiffel中的概念,[Wiener,1955]是学习Eiffel的一本好书。

    l          Grady Booch Ivar Jacobson Jim Rumbaugh

    这三个人物一同被冠以“The Three Amigos”的绰号。尽管他们在面向对象领域都有各自的主张,但在20世纪90年代后期,他们通力合作将面向对象用合理的符合表示,产生了统一建模语言(Unified Modeling LanguageUML),这个图形化的建模语言既有可视的表达形式,又有严谨的语义支撑。UML建模工具比其它表示法更直接和精确,实际上,你不应被混杂的符合表示法所吓倒。

     

    面向对象的成熟期

    老生物学家们常常发表这样的言论:个体生物重演了生物发展史。其含义是个体生物胚胎的发展通常演绎着生物整体发展的进化过程(如人类胚胎的发育过程)。当然在时间上存在很大的差异。个体生物的发展可能只有几个月,而整体生物却世代繁衍。

    尽管老生物学家的说法勿庸置疑,但是软件工程中却出现了一种新的观点:面向对象软件工程的历史重演了传统软件工程的历史。当然在时间跨度上也存在很多的差异。形成成熟的过程和数据库结构花费了几十年的时间,而钻研面向对象软件只有几年的时间。

    软件开发几乎是从编程开始的。随着系统规模扩大和人们经验的不断丰富,人们意识到编写应用软件代码仅仅依靠个人水平是有问题的。即使这样开发出来的应用程序可以奇迹的运行,但由于代码缺乏规范而使得对代码的任何修改几乎是不可能的。

    由此引入设计。软件设计是在编写代码之前,对代码的相关部分进行规划。这种根本的改进甚至可使人们解决潜在地维护问题。

    到目前为止一起顺利,现在可以生产出精致的软件,但一些敏锐的人发现这些精致的软件大部分不能够满足客户的需求。为满足用户对于软件可用性的不断追求,有规律地以及更加严格的分析方法产生了。

    最终,我们幸运的得到计算机辅助软件工程(ComputerAided Software EngineeringCASE)工具。起初这些工具的名声并不乐观。但随着工具的改进,逐渐改变了状态并在联邦保护软件(Federal Protection Program)中恢复了名誉。今天,以前的CASE工具称为自动建模工具。建模工具帮助我们进行需求分析、软件设计和软件构造,并且使软件开发和维护更加便于管理。

    在软件发展的整个历史中,人们一直试图提高软件的可重用性。但不幸的是大多数代码过程单元不是独立的,因此难以独立地重用。时至今日,面向对象的应用使得软件重用获得生机。

    然而面向对象并不是万能的,如果对象的类不是精心设计,那么面向对象也不可能提供可重用和可靠的软件。如前所述,面向对象的历史是与软件的主流史并行发展的。然而对面向对象而言,从实现到抽象的发展异常迅速。面向对象编程在20世纪80年代开始流行。在同一时期引进了面向对象设计和面向对象分析。大约在90年代出现了面向对象的数据库管理系统(ODBMS)和面向对象建模工具。

    面向对象领域的迅猛发展使人们患上奇怪的健忘症。一些人经历了面向对象的个体发展而一时忘记整个软件主流的发展。他们的口号是“1990年以前的任何知识都不必了解!”他们是热血而又执着的面向对象革命者。谴责当时使用已过时的COBOL语言建立的软件就像纸老虎,而认为通向叶卡特琳堡的路只有一条。现在他们的革命热情有所降温。对象革命已经取得成功,其煽动者和拥护者自身都已成为建设者。现在软件市场上的许多工具和技术或多或少地依赖面向对象技术。客户/服务器领域和其它分布式系统更是如此。

    软件王国总是充满着革命。下一场解放程序员的革命,分布式组件软件已经影响着我们,从一些热烈的演讲中得知,使用分布式组件可以获得比起初面向对象革命更多的益处。

     

    类似工程学的面向对象

    20世纪80年代,Brad Cox用某种类似现代生活中用到的硬件集成电路(IC)的方式看待软件对象。当华盛顿大学的考古学家们在我办公室的一堆论文中寻找时,我想起了这个类比。他们发现一本Merrill Skolnik著的《雷达系统导论》(Introduction to Radar System),在书中Skolnik先生提出下面的观点,“电子工程可以根据以下分类:组件;技术;系统。组件是可以组合的基本构造单元,通过使用适当地技术而产生系统。”如果对上面的一段话进行一下替换,用“软件”替换“电子”,用“类”替换“组件”,则变成下面的描述:“软件工程可以根据以下分类:类;技术;系统。类是可以组合的基本构造单元,通过使用适当地技术而产生系统。之后我们讨论的软件构件类似电子印刷电路板,这种构架实际上称为组件(Component)。

    尽管这种想法比较吸引人,但我们不能忘记,选择有用的电路封装在芯片中有赖于工程师对电路的正确标识。人们会冲出去购买IC用于操作放大器、音频放大器、设计器、线驱动器等等,但没有人愿意去购买晶体管、感应器及电阻的超大规模IC从头做起。在制成第一个有用的IC之前,继电子系统之后,工程师经过了几十年才意味的发现了这个有用的解决方案。

    同样,在软件中我们必须确保开发的类有效、健壮、易于抽象。例如Customer类和可爱的Stack类可能大受欢迎;而Egabragrettu类则可能令人想到路边的垃圾。

    Skolnik先生的第二个观点是关于技术的。因为,IC不能被组合则几乎是无用的,幸运的是电子工程师已经生产出将IC集成在一起的印刷电路板。

    类似地,开发面向对象软件,必须进行“宏“一级的设计,在这个层次上处理类(及类在运行时产生的对象)之间的联系。显然类的内部设计与更高层次的类间设计紧密相关。道理很简单,因为PCB的布局依赖于集成在其上的IC的设计程度。

    在类的内部层次和类间层次上都存在着面向对象设计的优劣。因此,好的面向对象系统与好的电子系统一样,不仅取决于高质量的抽象而且取决于这些抽象的高水平技术。

     

    面向对象的益处

    这个题目既迎合愤世嫉俗者也符合盲从者。

    一些反对者可能会说面向对象没有什么优点;它仅是一种流派或是一场从西方一些地区引发的全球性的阴谋。而一些激进派则宣称面向对象是一流的并且是所有软件成功的唯一途径。面向对象不仅适用于Windows系统,而且还适用于无所不能的分布式web体系结构。

    这两种说法都太极端,作者认为面向对象是有用的,但是不能神化,它还不够完美,其特定实用程序依赖于在软件开放过程中的使用方法。

    没有一种有价值得软件工程方法可以成为“当年时尚(Fad of the Year)”。当年时尚指某种方法在几个月或一年内变得十分流行。盲从者歇斯底里地指望“当年时尚”可以解决所有的软件问题。怀疑论者则给盲从者泼冷水而坐等其观。当不加选择地使用这种方法儿效果平平后,盲从者则放弃这种方法蜂拥般地转向下一个当年时尚。如果你的企业在“技术的浪尖上摇摆”,则应马上扭转盲从地局面,可能会从面向对象的技术中获得一些收益。

    面向对象不是万能的解决方案,愚蠢的解决问题方案也会使得你的企业步入困境。面向对象尽管充满挑战,但确实是一种有效的软件开发方法。一个成熟而职业化的企业不应该以极端的方法对待面向对象,而应该认真研究面向对象方法并将面向对象纳入开发专业软件的长期计划中。

    面向对象对企业的六个主要软件活动存在内在的影响。

    1.      用户需求分析。

    结构化技术的过程分析和数据分析之间的边界在哪里从未真正解决。数据流程图的过程世界与实体关系图的数据世界难以共存。过程和数据分析在某种场合可以满足要求,而在某些场合就会发生冲突。这种冲突在实时系统模型中尤为突出,如控制过程与数据模型的对应关系往往变得不清晰。

           面向对象方法在生命周期的早期就将过程和数据研究融合在一起。尽管不能明确地称为“过程和数据分析”,但在谈论面向对象时称“动态和静态的分析”更为妥当,使用面向对象概念将这两个方面的分析很好的协调起来。难怪有人将面向对象中过程和数据的融合比作Einstein的相对论中空间和时间的融合,尽管这种比喻有些言过其实。

    2.      软件设计

    在软件设计中,面向对象既有优势也有不足。

    面向对象的优势是使得设计者将软件中棘手的问题利用封装特性隐藏起来,这些问题包括:费解的数据结构、复杂的组合逻辑、详细的过程和数据之间的关系、高深的算法以及可怕的设备驱动程序。

    面向对象的缺点是应用封装和继承特性使结构本身变得复杂。在面向对象中很难创建一个戈尔地雅斯吊床难结,要么不可建立,要么使得系统运行像一匹负重的赛马。避免出现这些问题正是面向对象设计者所面临的挑战。尽管面向对象的设计有时是非常艰辛的,一旦完成它,对处理大量复杂单元所带来的益处要多于采用其它的设计技术。

    3.      软件构造

    采用面向对象方法建立系统最常考虑的质量要素为:可重用性、可靠性、健壮性、可扩展性、分布性和可存储性。

    4.      软件维护

    可重用性、可靠性、健壮性、可扩展性是软件维护的四大支柱。许多企业在软件维护上花费很高。由于面向对象可以提高这四个方面的质量特征,因此能在以下方面降低系统的维护开销:

    可重用性降低了企业整个代码的维护费用。减少编写新代码及以后系统维护的量,特别是开头一两个项目后效果特别明显。

    可靠性减少了用户的不满意和对修正问题的痛苦抱怨。

    </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

    <script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

    最新回复(0)