




已阅读5页,还剩21页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
程序设计方法的演化及极限1引言 当今社会是信息社会,信息社会的灵魂是作为“信息处理机”的电子计算机,从1946年第一台计算机ENIAC问世到今天的“深蓝”,电子计算机的硬件得到突飞猛进的发展,程序设计的方法也随之不断的进步。20世纪70年代以前,程序设计方法主要采用流程图,结构化设计(StructureProgramming,SP)思想也趋成熟,整个20世纪80年代SP是主要的程序设计方法。然而,随着信息系统的加速发展,应用程序日趋复杂化和大型化。传统的软件开发技术难以满足发展的新要求。20世纪80年代后,面向对象的程序设计(ObjectOrientProgramming,OOP)技术日趋成熟并逐渐地为计算机界所理解和接受。面向对象的程序设计方法和技术是目前软件研究和应用开发中最活跃的一个领域。 如果把过去十年称为“OO时代”,相信不会引起太多的异议。面向对象技术会如此受到广泛的重视,主要是面向对象的思想接近于客观世界的实际和符合人们通常的思维方式从而易于为人们所接受。到目前为止,OO技术得到了最广泛的应用,并且也被证明不是软件开发的“银弹”。从RobertMartin到BertrandMeyer,研究者们把OO的能力和局限摸了个一清二楚。同时,人们也在传统的OO三要素(封装性,继承性,多态性)的基层上发展了更多的新技术,借以弥补OO的缺陷,使OO方法和技术能够更好地解决软件开发中的问题。 OO技术的一大局限性就是:它对软件职责的划分是“垂直”的。在一个标准的对象继承体系中,每一继承类主要是负责软件系统中一个特定部分的功能,对象的行为是在编译期间被决定的。OO技术的另一大问题是接口问题。在传统的OO环境下,对象开发者没有任何办法确保使用者按照自己的要求来使用接口。由于OO技术的这些缺点,按照事物发展的规律,必然促使新的程序设计方法出现来解决这些问题,这就是即将到来的后OO时代。 2结构化程序设计 早期的计算机存储器容量非常小,人们设计程序时首先考虑的问题是如何减少存储器开销,硬件的限制不容许人们考虑如何组织数据与逻辑,程序本身短小,逻辑简单,也无需人们考虑程序设计方法问题。与其说程序设计是一项工作,倒不如说它是程序员的个人技艺。但是,随着大容量存储器的出现及计算机技术的广泛应用,程序编写越来越困难,程序的大小以算术基数递增,而程序的逻辑控制难度则以几何基数递增,人们不得不考虑程序设计的方法。 最早提出的方法是结构化程序设计方法,其核心是模块化。1968年Dijskstra在计算机通讯上发表文章1,注意到了“结构化程序设计”,之后,Wulf主张“可以没有GOTO语句”2。至1975年起,许多学者研究了“把非结构化程序转化为结构化程序的方法”,“非结构的种类及其转化”,“结构化与非结构化的概念”,“流程图的分解理论”等问题。结构化程序设计逐步形成既有理论指导又有切实可行方法的一门独立学科。 SP方法主张使用顺序、选择、循环三种基本结构来嵌套连结成具有复杂层次的“结构化程序”,严格控制GOTO语句的使用。用这样的方法编出的程序在结构上具有以下效果: (1)以控制结构为单位,只有一个入口,一个出口,所以能独立地理解这一部分。 (2)能够以控制结构为单位,从上到下顺序地阅读程序文本。 (3)由于程序的静态描述与执行时的控制流程容易对应,所以能够方便正确地理解程序的动作。 SP的要点是:“自顶而下,逐步求精”的设计思想,“独立功能,单出入口”的模块仅用3种(顺序、分支、循环)基本控制结构的编码原则3。自顶而下的出发点是从问题的总体目标开始,抽象低层的细节,先专心构造高层的结构,然后再一层一层地分解和细化。这使设计者能把握主题,高屋建瓴,避免一开始就陷入复杂的细节中,使复杂的设计过程变得简单明了,过程的结果也容易做到正确可靠。独立功能,单出、入口的模块结构减少了模块的相互联系使模块可作为插件或积木使用,降低程序的复杂性,提高可靠性。程序编写时,所有模块的功能通过相应的子程序(函数或过程)的代码来实现。程序的主体是子程序层次库,它与功能模块的抽象层次相对应,编码原则使得程序流程简洁、清晰,增强可读性。 在SP中,划分模块不能随心所欲地把整个程序简单地分解成一个个程序段,而必须按照一定的方法进行。模块的根本特征是“相对独立,功能单一”。换言之,一个好的模块必须具有高度的独立性和相对较强的功能。模块的好坏,通常用“耦合度”和“内聚度”两个指标从不同侧面而加以度量。所谓耦合度,是指模块之间相互依赖性大小的度量,耦合度越小,模块的相对独立性越大。所谓内聚度,是指模块内各成份之间相互依赖性大小的度量,内聚度越大,模块各成份之间联系越紧密,其功能越强。因此在模块划分应当做到“耦合度尽量小,内聚度尽量大”。 转贴于 265考试网 http:/www.265ks.c结构化程序相比于非结构化程序有较好的可靠、易验证性和可修改性;结构化设计方法的设计思想清晰,符合人们处理问题的习惯,易学易用,模块层次分明,便于分工开发和调试,程序可读性强。其设计语言以Ada语言为代表。3面向对象程序设计 20世纪90年代,由于计算机硬件的飞速发展,对软件系统在规模和性能方面的要求也在不断的提高。因此,传统的程序设计方法使得软件和硬件能力的差距迅速扩大。传统的软件工具、软件技术和抽象层次越来越难以适应大规模复杂软件系统的开发特点。因此,软件能力已成为制约软件发展的主要因素。 OOP方法源于20世纪70年代中后期,在20世纪80年代逐步代替了传统的SP方法,成为最重要的方法之一,至今OOP方法被广泛应用于各个领域。面向对象的基本思想与结构化设计思想完全不同,面向对象的方法学认为世界由各种对象组成,任何事物都是对象,是某个对象类的实例,复杂的对象可由较简单的对象的某种方式组成。OOP的基石是对象和类。对象是数据及对这些数据施加的操作结合在一起所构成的独立实体的总称;类是一组具有相同数据结构和相同操作的对象的描述。面向对象的基本机制是方法和消息,消息是要求某个对象执行类中某个操作的规格说明;方法是对象所能执行的操作,它是类中所定义的函数,描述对象执行某个操作的算法,每一个对象类都定义了一组方法。 OOP有3个重要特性:封装性、继承性和多态性。封装性是指对象是数据和处理该数据的方法所构成的整体,外界只能看到其外部特性(消息模式、处理能力等),其内特性(私有数据、处理方法等)对外不可见。对象的封装性使得信息具有隐蔽性,它减少了程序成分间的相互依赖,降低程序的复杂性,提高程序的可靠性和数据的安全性。继承性(Inheritance)反映的是类与类之间的不同抽象级别,根据继承与被继承的关系,可分为基类和衍生类,基类也称为父类,衍生类也称为子类,正如“继承”这个词给我们的字面提示一样,子类从父类那里获得所有的属性和方法,并且可以对这些获得的属性和方法加以改造,使之具有自己的特点。继承性使得相似的对象可以共享程序代码和数据,继承性是程序可重用性的关键。多态性是指在形式上表现为一个方法根据传递给它的参数的不同,可以调用不同的方法体,实现不同的操作。将多态性映射到现实世界中,则表现为同一个事物随着环境的不同,可以有不同的表现形态及不同的和其他事物通信的方式。多态性使程序员能在一个类等级中使用相同函数的多个版本,程序员可以集中精力开发可重用的类和方法而不必过分担心名字的冲突问题。 OOP方法是以“对象”为中心进行分析和设计,紧抓“模型化世界”的对象,使这些对象形成了解决目标问题的基本构件,即解决从“用什么做”到“要做什么”。其解决过程从总体上说是采用自底向上方法,先将问题空间划分为一系列对象的集合,再将对象集合进行分类抽象,一些具有相同属性行为的对象被抽象为一个类,类还可抽象分为子类、超类(超类是子类的抽象)。其间采用继承来建立这些类之间的联系,形成结构层次。同时对于每个具体类的内部结构,又可采用自顶向下逐步细化的方法由粗到细精化之。调试运行时通过向类对象发消息来完成,对象执行相应操作并返回结果,使对象集的初始状态变成了终态。故OOP总体来说主要是不断设计新的类和创建对象的过程。 与传统的SP相比,OOP方法具有许多优点,如采用“对象”为中心的设计方式,再现了人类认识事物的思维方式和解决问题的工作方式;OOP方法以对象为唯一的语义模型,整个软件任务是通过各对象(类)之间相互传递消息的手段协同完成。因此,能尽量逼真地模拟客观世界及其事物;由对象和类实现了模块化,类继承实现了抽象对象,以及任一对象的内部状态和功能的实现的细节对外都是不可见的,因此很好地实现信息隐藏。由此建立在类及其继承性基础上的重用能力可应付复杂的大型的软件开发。面向对象方法使得软件具有良好的体系结构、便于软件构件化、软件复用和良好的扩展性和维护性,抽象程度高,因而具有较高的生产效率。目前,面向对象程序设计语言以Java、C+为代表。4后面向对象方法 虽然面向对象程序设计方法有诸多优点,但经过多年的实践摸索,人们也发现面向对象方法有其不足,如许多软件系统不完全都能按系统的功能来划分构件,仍然有许多重要的需求和设计决策,比如安全、日志等,它们具有一种“贯穿特性”(crosscuttingconcerns),无论是采用面向对象语言还是过程型语言,都难以用清晰的、模块化的代码实现。最后的结果经常是:实现这些设计决策的代码分布贯穿于整个系统的基本功能代码中,形成了常见的“代码散布”(codescattering)和“代码交织”(codetangling)现象。代码交织现象是现有软件系统中许多不必要的复杂性的核心。它增加了功能构件之间的依赖性,分散了构件原来假定要做的事情,提供了许多程序设计出错的机会,使得一些功能构件难以复用,源代码难以开发、理解和发展。 因此,人们在面向对象的基础上发展了更多的新技术,借以弥补面向对象技术的缺陷,使得面向对象技术能够更好的解决软件开发中的问题。这些建立在面向对象的基础上、并对面向对象做出扩展的新技术被广泛应用的时期,我们把它称为“后面向对象时代”。在后面向对象时代,有许多新型的程序设计思想值得关注。 4.1面向方面程序设计 面向方面程序设计(Aspect-Orientedprogramming,AOP)方法,这一概念最早是由施乐(Xerox)公司在美国加州硅谷PaloAlto研究中心(PARC)的首席科学家、加拿大大不列颠哥伦比亚大学教授GregorKicgales等人首次在1997年的欧洲面向对象编程大会(ECOOP97)上提出4。 所谓的Aspect,就是AOP提供的一种程序设计单元,它可以将上文提到的那些在传统程序设计方法学中难于清晰地封装并模块化实现的设计决策,封装实现为独立的模块。Aspect是AOP的核心,它超越了子程序和继承,是AOP将贯穿特性局部化和模块化的实现机制。通过将贯穿特性集中到Aspect中,AOP就取得一种单一的结构化行为-该行为在传统程序中分布于整个代码中-这样就使Aspect代码和系统目标都易于理解。在AOP中,Aspect是AOP中的一阶实体,AOP中的Aspect正如OOP中的类。现有对Aspect的认识有错误校验策略、设计模式、同步策略、资源共享、分布关系和性能优化等。 Aspect的实现和传统开法方法中模块的实现不同。Aspect之间是一种松耦合的关系,各Aspect的开发彼此独立。主代码的开发者甚至可能没有意识到Aspect的存在,只是在最后系统组装时刻,才将各Aspect代码和主代码编排融合在一起。因此,主代码和Aspect之间可以是一种不同于传统“显式调用”关系的“隐式调用”5。在软件复杂性日益增加的今天,隐式调用有巨大的优点,因为某一应用的领域专家,不太可能对分布、认证、访问控制、同步、加密、冗余等问题的复杂的实现机制很熟悉,因此就不能保证他们在程序中进行正确的调用。在当前强调程序演化的情况下,这一点尤其重要,因为开发人员很难正确预见到未来对程序的新需求。 AOP是一种关注点分离技术,通过运用aspect这种程序设计单元,允许开发者使用结构化的设计和代码,反映其对系统的认识方式。要使设计和代码更加模块化、更具结构化,使关注点局部化而不是分散于整个系统中。同时,需使关注点和系统其他部分保持良好定义的接口,从而真正达到“分离关注点,分而治之”的目的。 有关AOP更加详细的资料,读者可以进入希赛网()的“技术应用”频道中的“IT新技术”栏目,阅读相关的文章。 4.2面向Agent程序设计 随着软件系统服务能力要求的不断提高,在系统中引入智能因素已经成为必然。Agent作为人工智能研究重要而先进的分支,引起了科学、工程、技术界的高度重视。斯坦福大学的BarbaraHayes-Roth在IJCAI95的特约报告中提及:智能的计算机主体既是人工智能最初的目标,也是人工智能最终的目标。 在计算机科学主流中,Agent的概念作为一个自包含、并行执行的软件过程能够封装一些状态并通过传递消息与其它Agent进行通信,被看作是面向对象程序设计的一个自然发展。Agent具有以下主要特征: (1)代理性(ActionOnBehalfOthers)。代理具有代表他人的能力,即它们都代表用户工作。这是代理的第一特征。 (2)自制性(Autonomy)。一个代理是一个独立的计算实体,具有不同程度的自制能力。它能在非事先规划、动态的环境中解决实际问题,在没有用户参与的情况下,独立发现和索取符合拥护需要的资源、服务等等。 (3)主动性(Proactivity)。代理能够遵循承诺采取主动,表现面向目标的行为。例如,Internet上的代理可以漫游全网,为用户收集信息,并将信息提交给用户。 (4)反应性(Reactivity)。代理能感知环境,并对环境作出适当的反应。 (5)社会性(SocialAbility)。代理具有一定的社会性,即它们可能同代理代表的用户、资源、其它代理进行交流。 (6)智能性(Intelligence)。代理具有一定程度的智能,包括推理到自学习等一系列的智能行为 (7)移动性(Mobility)。代理具有移动的能力,为完成任务,可以从一个节点移动到另一个节点。比如访问远程资源、转移到环境适合的节点进行工作等。 面向Agent的程序设计(Agent-Orientedprogramming)方法与面向对象程序设计方法的最基本区别在于Agent的社会性。面向Agent程序设计的主要思想是:根据Agent理论所题推出的代表性Agent特性的、精神的和有意识的概念直接设计Agent。这样一个目的后面的动机是人们运用意愿姿态作为一个代表复杂系统的抽象机制。由于Agent的上述特性,基于Agent的系统应是一个集灵活性、智能性、可扩展性、稳定性、组织性等诸多优点于一身的高级系统。 4.3其它后面向对象程序设计 除了上述两种主流的后面向对象程序设计方法外,还出现了许多值得关注的新的程序设计方法。如: (1)泛型程序设计(genericProgramming,GP)。GP是一种范型(paradigm)它致力于将各种类型按照一小组功能性的需求加以抽象,然后以这些需求为条件实现算法。由于算法在其操作的数据类型上定义了一个严格的窄接口,同一个算法便可以应用于各种类型之上。GP为应用程序开发人员提出了十分美妙的承诺。它使“从一种一个的软件系统向自动制作软件的各不相同的变体发展”这种思路变得十分真实可信。简单地说,GP以“确定软件开发中自动化的好处”为中心进行软件开发。 (2)面向构件程序设计。在面向构件程序设计中构件就是一组业务功能的规格,面向构件针对的是业务规格,不需要源代码,可执行代码或者中间层的编译代码,在这个层面上可以做到代码的集成、封装、多态,做到AOP,这才是面向构件的精髓。面向构件技术还包括了另一个重要思想,这就是程序在动态运行时构件的自动装载。 (3)敏捷方法(AgileMethodologies)。敏捷也称作轻量级开发方法,对许多人来说,这类方法的吸引之处在于对繁文缛节的官僚过程的反叛。它们在无过程和过于繁琐的过程中达到了一种平衡,使得能以不多的步骤过程获取较满意的结果。敏捷型方法强调“适应性”而非“预见性”,敏捷型方法变化的目的就是成为适应变化的过程,甚至能允许改变自身来适应变化。敏捷型方法是“面向人”的(people-oriented)而非“面向过程”的(process-oriented),敏捷型方法认为没有任何过程能代替开发组的技能,过程起的作用是对开发组的工作提供支持。5程序设计方法的极限 软件工程发展的一个侧重方向是对软件开发过程中分析、设计的方法的研究。这方面的第一个重要成果就是在70年代风靡一时的结构化开发方法,即PO(面向过程的开发或结构话方法)。PO是人们在用计算机世界来表达现实世界时,追求过程话、模块化、封装以及更高的抽象的结果。人们用计算机来映射现实世界时,最低层的实现无非是靠数字电路技术产生的高电平与低电平信号。在PO中,人们关注的是如何用函数和过程来实现对现实世界的模拟,将其映射到计算机世界之中。OO是这种抽象层次不断提高的过程的自然发展结果,它采用类和对象的概念,把变量以及对变量进行操作的函数和过程封装在一起,用这种更高一级的抽象来表达客观世界。通常,一个对象包含一些属性和方法,它对应于自然语言中一个有意义的名词,描述了现实世界中的一个物体(物理实体)或概念(抽象实体)。 我们知道,软件工程的发展历史就是人们不断追求更高的抽象、封装和模块化的历史。OO当然不会是历史的终结。尽管不能精确得到OO之后是什么,我们至少可以推知,OO之后的XO,必然将是比OO更高一级的抽象。它所依赖的核心概念必然高于并包容对象这一概念。正如对象高于并包容了函数和变量一样。 OO之后是什么呢?可能是FO-FunctionOriented(面向泛函)。这里的Function不同于我们在当前编程所用的函数Function,这里的Function指职能模块高级抽象。所谓职能模块,是指可独立完成特定任务,而对无力完成的任务可自行找到具备完成该任务功能的其它职能模块,并与之建立联系以合力完成工作的功能体。FO需要高速智能网络、智能接口、分布式技术、并行技术,最重要的是需要一个国际化的管理机构。 假如,OO之后是FO,那么我们当然要问FO之后会是什么呢?再往下又会是什么,有没有一个极限呢?如果有极限是什么呢?如果从计算机和数学理论回答这个问题是很困难的,但是,我们换个角度,以哲学的观点来分析这个问题。现实世界中的任何事物都有其发生、发展、成熟和灭亡的过程,假如把程序设计方法是一个事物,那么它也应该有其发生、发展、成熟和灭亡的经历。正如最初是不存在程序设计方法这个概念,随着计算机硬件的发展,出现了SP方法,紧接着就是OOP方法,到后OOP时代,程序设计方法也遵循着这样一个自然规律。也就是说,程序设计方法肯定是有其极限的,可能若干年后我们所需要的不在是程序设计方法这样一个概念了,而是在更抽象的层次上智能的生产软件。现在让我们假设软件工程已经发展到了这样一个理想的境界,有一天我们实现了自然语言编程,是否就万事大吉了?换句话,自然语言是否能很好地描述、表达客观感知世界?维特根斯坦在逻辑哲学论里已经指出:世界的意义必定存在于世界之外;实际上存在着不可表达的东西;这显示了它的存在,它是神秘的;也就是说,外部世界中存在一些我们可以感知却无法用语言来表达的东西;“对于那些不可言说的,必须保持沉默”这句话,成为我们最后的极限。论程序设计方法如果你是初学者-请不要阅读; 但有志成为中高级程序员-请务必阅读; 如果你是中级程序员-请务必阅读; 如果你高级程序员-请批评指正。 本文是我在“软件工程师班”开学第一节课的讲义,和“计算机软件设计发展”讲座上的内容整理而成。写作本文的目的是引导学生从更高的层次来看待程序设计方法,为将来成为高级程序员而做好理论准备。 一、计算机硬件环境对软件设计方法的限制 计算机的发明到现在已经60年了,计算机程序设计方法也伴随着计算机硬件技术的提高而不断发展。硬件环境对软件设计既有严重的制约作用,也有积极的推动作用。 在我的大学母校(此处删除6个字),数学系的一些老师,有幸成为了我国第一代的计算机DIY一族。呵呵,不要以为是组装PC机呦,他们组装的可是小型机。一人多高铁皮柜大小的主机,加上纸带机(后期改进为读卡机),组装好后,除了供学校自己的科研使用外,还在全国各地销售了十几台。当时(七十年代)一台的售价是10几万元人民币,如果换算到今天,相当于价值大约为100多万元,非常高档的小型计算机了。下面大家猜猜,这么高档的计算机,它的内存是多少那?(都把嘴闭好了,我要公布答案了) 4K。 一块50公分见方的内存板, 插入到主机箱中,好了- 1K; 再插一块内存板,好了- 2K; 再插一块内存板,好了- 3K; 再插一块内存板,好了- 4K; 再.不行了,插不起了,太贵了!这就是当时的环境。这样的环境下,用什么写程序那?当然只有机器码了。先用汇编写,然后翻阅手册手工改写为机器码,然后打卡或穿纸带,输入运行。可以想象,在当时的条件下,什么叫好的程序那?什么叫优秀的程序那? 技巧! 程序设计的最初始阶段,是讲究技巧的年代。如何能节省一个字节,如何能提高程序运行的效率,这些都是要严肃考虑的问题。而所谓的程序的易读性,程序的可维护性根本不在考虑范围之内。 今天,35岁以上的学习过计算机的朋友可能都使用过一种个人计算机APPLE-II(中国也生产过这种计算机的类似产品“中华学习机”)。主频1M,内存48K(扩展后,最多可达到64K)。我就是使用这样的计算机长大的 :)。当年,类似的个人计算机产品,还有PC1500,Layser310等。这种计算机上已经固化了 BASIC 语言,当然只是为学习使用。要想开发出真正的商业程序,则必须使用汇编,否则的话,程序就比蜗牛还要慢了。于是,程序设计中对于技巧的运用,是至关重要的了。 题外话1: 比尔盖茨是 BASIC 的忠实拥护和推动者。当年,他在没有调式环境的状况下,用汇编语言写出了一款仅有 4K 大小的 BASIC 解释器,且一次通过。确实令人佩服。(不象现在微软出品的程序,动辄几十兆。)这也许就是比尔对 BASIC 情有独钟的原因,每当微软推出(临摹)一个新技术,则他会立刻在 BASIC 中提供支持。 题外话2: 在 APPLE-II 上有一款游戏软件“警察抓小偷”,当年熬夜玩游戏,乐趣无穷。后来这款游戏被移植到了PC上,咳根本没有办法玩,因为小偷还没跑就被警察抓到了。硬件的速度提升,另我无法再回味以前的时光了。二、结构化程序设计 随着计算机的价格不断下降,硬件环境不断改善,运行速度不断提升。程序越写越大,功能越来越强,讲究技巧的程序设计方法已经不能适应需求了。记得是哪本书上讲过,一个软件的开发成本是由:程序设计 30% 和程序维护 70% 构成。这是书上给出的一个理论值,但实际上,从我十几年的工作经验中,我得到的体会是:程序设计占 10%,而维护要占 90%。也许我说的还是太保守了,维护的成本还应该再提高。下面这个程序,提供了两种设计方案,大家看看哪个更好一些那? 题目:对一个数组中的100个元素,从小到大排序并显示输出。(BASIC) 方法1:冒泡法排序,同时输出。 FOR I=1 TO 100 FOR J=I+1 TO 100 IF AI AJ THEN T=AJ: AJ=AI: AI=T NEXT J ? AINEXT I 方法2:冒泡法排序,然后再输出。FOR I=1 TO 100FOR J=I+1 TO 100IF AI AJ THEN T=AJ: AJ=AI: AI=TNEXTNEXTFOR I=1 TO 100? AINEXT 显然,“方法1”比“方法2”的效率要高,运行的更快。但是,从现在的程序设计角度来看,“方法2”更高级。原因很简单:(1)功能模块分割清晰易读;(2)也是最重要的易维护。程序在设计阶段的时候,就要考虑以后的维护问题。比如现在是实现了在屏幕上的输出,也许将来某一天,你要修改程序,输出到打印机上、输出到绘图仪上;也许将来某一天,你学习了一个新的高级的排序方法,由“冒泡法”改进为“快速排序”、“堆排序”。那么在“方法2”的基础上进行修改,是不是就更简单了,更容易了?!这种把功能模块分离的程序设计方法,就叫“结构化程序设计”。三、对程序设计中技巧使用的思考 我可以肯定,大家在开始学习程序设计的时候,一定都做过这样一个题目:求100以内的素数。老师在黑板上,眉飞色舞地写出了第一个程序:(C程序) 方法1:for(i=1; i100; i+) for(j=2; j= i) printf(%d, i); 然后,老师开始批判这个程序“这个叫什么呀?太慢了!因为我们都知道大偶数不可能是素数了,因此,要排除掉!” 于是,意尤未尽地写出了第二个程序: 方法2:printf(2,);for(i=3; i100; i+=2) for(j=2; j= i) printf(%d, i); 老师说:“看!我们只改动了一点点,程序运行的速度就提高了一倍多”。然后运用诱导式教学法继续提问“程序的效率,还能再提高吗?能!”,得意地写出第三个程序: 方法3:printf(2,);for(i=3; i100; i+=2) 不考虑大偶数 for(j=3; j= i) printf(%d, i); “大家看,我们又只改动了一点点,运行速度又提高了一倍多。可以了吗?不可以!我们还能再提高”。于是又高傲地写出了第四个程序: 方法4:printf(2,);for(i=3; i100; i+=2) int k = sqrt(i); for(j=3; j= k ) printf(%d, i); 然后,开始证明为什么我们判断素数的时候,只需要验算到平方根就足够了: 假设p是合数,那么令:p=a*b。反正法:由于我们已经判断了p的平方根以内的整数都不能被p整除,于是 aSQRT(p)。基于同样的理由 bSQRT(p)。于是 p = a * b SQRT(p) * SQRT(p) = p 得出矛盾, 命题得正。 的确,“方法4”的确比“方法1”的运行速度要提高了好几倍,甚至好几十倍。但我们仔细分析测试看看。 (1)“程序4”到底比“程序1”快了多少那?我在某台计算机上进行测试(P4,1.5G)得到的速度对比表:计算范围100100010000100000速度差0.00秒0.01秒 0.18秒15秒 (2) 在10万以上,才会看出一些差别。而这种差别根本就不够底偿程序设计阶段的付出。如果计算的范围再大,那么不管是“方法1”,还是“方法4”都不是好的算法。(计算素数的另外一个比较优秀的算法叫“漏筛法”) (3)写出“方法1”,只要具有小学四年级的数学水平就够了,而“方法4”则需要初中三年级的水平并且还要具备一些“数论”的知识。 (4)从维护性看,如果你写的程序需要另外一个程序员来维护,或者若干时间以后,你重新来阅读这段程序,那么就会对这个程序产生很多疑问:这个求平方根是干什么用的?其实,就这个题目来说,使用到“方法3”就已经足够了。 总结发言: I. 计算机的价格每年下降一半,而运算速度每年提高一倍”,因此我们应该把速度提高的任务交给硬件实现。 II. 从易读性、维护性出发,程序员只负责按定义给出软件实现。算法的问题是数学家解决的。 题外话: 多年以来,人们一直在寻找动态图象(影视)的存储和回放的算法,但效果都不理想。直到有人发现,原来在200多年前的数学家早就帮我们解决了这个问题傅立叶(Fourier)级数展开。因此我要说,优秀的算法不是我们程序员要考虑的问题,我们的任务只要按照数学家给出的算法翻译为计算机程序语言而已。(这句话恐怕要遭到大多数程序员抛出的板砖袭击)再比如,计算一元多次方程解的问题。我们使用的就是牛顿的迭代算法。不要怪我瞧不起你,你能发明这个方法的话,那就是当代的牛顿了。四、程序的易读性与书写方法 程序是否容易阅读和维护,与怎么书写有很大的关系。说实在的,C语言中为了方便程序员书写,允许使用+,-,&,?.这些运算符号。但很多人经常乱用,以为自己写的程序多么简洁,效率多高。其实,当你分行书写的话则更加容易阅读和维护,效率也不会降低,因为编译程序早就帮你优化为最快捷的代码了。先看一个简单的例子: 计算一个整数乘 255(C语言) 方法1:a *= 255; 方法2:因为移位运算比乘法运算要快很多倍,因此a*255的运算书写为:a =(a8)-a; /a*255 = a*256 - a = (a8) - a 方法1的书写非常简单,直截了当,显然更容易维护。而方法2的书写运用了移位的技巧,不容易阅读,但效率最高。是不是真的是这样那?把这两个程序编译为汇编代码看看。原来无论是方法1还是方法2,它们的汇编代码都是一样的:mov ecx, eaxshl eax, 8sub eax, ecx 也就是说,你认为非常技巧的书写方法,其实编译器的优化功能早就帮你想到了。那么方法2的方式就很值得批判了。下面是几个有关C语言书写方面的重要原则: 1)尽量表达愿义,多加注释; 2)变量名称和函数名称,要使用有意义的符号,并且遵守“匈牙利命名法”; 3)不要为俭省内存,使一个变量在一个模块中表达多个含义。 4)在某个模块中,前半部分用i表示计数器,由于后半部分不再使用计数器了,于是又用i来保存某个中间的结果。等你维护这段程序的时候,保证你肯定会犯傻的。 5)在使用条件表达式的时候,不要混合书写运算表达式; 经常有人在书写for循环的时候,使用这样的方式: for(int a=1,s=0; a=100 & (s+=a); a+); 天呀,这样写是不会提高程序运行效率的,尤其是当运算表达式复杂的时候,就更不容易阅读了,还是把运算写到for的循环体中吧。 int s = 0;for(int a=1; a=100; a+) s += a; /计算1+2+.+100 这不很好吗?! 再比如,if(a=b)这个写法在语法上是允许的,但不要使用。要使用也要if(0!=(a=b)这样的方式。 还有值得一提的是慎用“,”(逗号运算符)。 不要连续使用+,-,,*,& .这样的运算符号。 a = b+-(-c1); /这个人有病。出这个题目考试的老师,也有病。 常量要写在条件表达式的左边; if(5 = a) 这是正确的写法,这样书写可以避免勿输入而导致的 if(a=5)这样的错误。 避免程序中 的嵌套层次太深; 最多4层。如果必须大于4层,那么写成调用子函数或宏的方式。 尽量多地使用断言; 当你在书写程序的过程中,凭你的智慧,你一定是知道:程序运行到我正书写的这行代码的时候某个变量一定是某个值。好啦,那么不要忧郁,马上加上一句代码:ASSERT(nnn = xxx);。将来在调式维护这段代码的时候,你会得到无限美妙的回报。 书写需要“成对匹配”使用的代码的时候,在写使用代码之前,就先把结束写出来;file.Open(.); /当要打开文件的时候 char *lp=new char 100; /当要申请内存的时候. /先不要写这段代码 . /先不要写这段代码file.Close(); /马上写关闭 delete lp; /马上写释放xxx.Loack(); /当某个对象需要锁定的时候 for(.). /先不要写这段代码 /写大括号的时候xxx.Unlock(); /马上写解锁 /马上写大括号结束 和这个道理相同,在C+的类中,如果需要申请内存,那么先在构造函数中给出 lp=NULL;然后马上在析构函数中书写 if(lp) delete lp; 可以适当地使用goto; 在结构化程序设计中,goto 是被排斥的。但是,如果适当地使用 goto 不但不影响斜率,而且还能提高程序的可读性。 题目:合并2个文件到一个新文件中。(不要挑我的毛病呀,我使用的是类C的方式书写的。)方法1: FILE *f1,*f2,*f3;if(Open(f1)成功) if(Open(f2)成功) if(Open(f3)成功) . /这里是真正干活的地方 Close(f1); Close(f2); Close(f3); else /f3不成功 Close(f1); Close(f2); . else /f2不成功 Close(f1); . else /f1不成功 . = 方法2:FILE *f1=NULL,*f2=NULL,*f3=NULL;if(Open(f1)不成功) goto err;if(Open(f2)不成功) goto err;if(Open(f3)不成功) goto err;. /这里是真正干活的地方err:if(f3) Close(f3);if(f2) Close(f2);if(f1) Close(f1); 方法1是最最标准的结构化设计,好吗?不好!尤其是当 的层次比较深的时候,估计你寻找真正干活的代码的地方都找不到。而使用方法2的程序,不但程序容易读,而且没有 的深度。在C+中,又提供了异常try/catch的设计结构,而异常的结构则比 goto 的结构更好、更完善了。五、面向对象的程序设计 随着程序的设计的复杂性增加,结构化程序设计方法又不够用了。不够用的根本原因是“代码重用”的时候不方便。面向对象的方法诞生了,它通过继承来实现比较完善的代码重用功能。很多学生在应聘工作,面试的时候,常被问及一个问题“你来谈谈什么是面向对象的程序设计”,学生无言,回来问我,这个问题应该怎么回答。我告诉他,你只要说一句话就够了“面向对象程序设计是对数据的封装;范式(模板)的程序设计是对算法的封装。”后来再有学生遇到了这个问题,只简单的一句对答,对方就对这个学生就刮目相看了(学生后来自豪地告诉我的)。为什么那?因为只有经过彻底的体会和实践才能提炼出这个精华。 面向对象的设计方法和思想,其实早在70年代初就已经被提出来了。其目的就是:强制程序必须通过函数的方式来操纵数据。这样实现了数据的封装,就避免了以前设计方法中的,任何代码都可以随便操作数据而因起的BUG,而查找修改这个BUG是非常困难的。那么你可以说,即使我不使用面向对象,当我想访问某个数据的时候,我就通过调用函数访问不就可以了吗?是的,的确可以,但并不是强制的。人都有惰性,当我想对 i 加1的时候,干吗非要调用函数呀?算了,直接i+多省事呀。呵呵,正式由于这个懒惰,当程序出BUG的时候,可就不好捉啦。而面向对象是强制性的,从编译阶段就解决了你懒惰的问题。 巧合的是,面向对象的思想,其实和我们的日常生活中处理问题是吻合的。举例来说,我打算丢掉一个茶杯,怎么扔那?太简单了,拿起茶杯,走到垃圾桶,扔!注意分析这个过程,我们是先选一个“对象”-茶杯,然后向这个对象施加一个动作扔。每个对象所能施加在它上面的动作是有一定限制的:茶杯,可以被扔,可以被砸,可以用来喝水,可以敲它发出声音.;一张纸,可以被写字,可以撕,可以烧.。也就是说,一旦确定了一个对象,则方法也就跟着确定了。我们的日常生活就是如此。但是,大家回想一下我们程序设计和对计算机的操作,却不是这样的。拿DOS的操作来说,我要删除一个文件,方法是在DOS提示符下:c: del 文件名。注意看这个过程,动作在前(del),对象在后(文件名),和面向对象的方法正好顺序相反。那么只是一个顺序的问题,会带来什么影响那?呵呵,大家一定看到过这个现象:File not found. “啊,我错了,我错了,文件名敲错了一个字母”,于是重新输入:c: del 文件名2。不幸又发生了,计算机报告:File read only. 哈哈,痛苦吧:)。所以DOS的操作其实是违反我们日常生活中的习惯的(当然,以前谁也没有提出过异议),而现在由于使用了面向对象的设计,那么这些问题,就在编译的时候解决了,而不是在运行的时候。obj.fun(),对于这条语句,无论是对象,还是函数,如果你输入有问题,那么都会在编译的时候报告出来,方便你修改,而不是在执行的时候出错,害的你到处去捉虫子。 同时,面向对象又能解决代码重用的问题继承。我以前写了一个“狗”的类,属性有(变量):有毛、4条腿、有翘着的尾巴(耷拉着尾巴的那是狼)、鼻子很灵敏、喜欢吃肉骨头.方法有(函数):能跑、能闻、汪汪叫.如果它去抓耗子,人家叫它“多管闲事”。好了,狗这个类写好了。但在我实际的生活中,我家养的这条狗和我以前写的这个“狗类”非常相似,只有一点点的不同,就是我的这条狗,它是:卷毛而且长长的,鼻子小,嘴小.。于是,我派生一个新的类型,叫“哈巴狗类”在“狗类”的基础上,加上新的特性。好了,程序写完了,并且是重用了以前的正确的代码这就是面向对象程序设计的好处。我的成功只是站在了巨人的肩膀上。当然,如果你使用VC的话,重用最多的代码就是MFC的类库。六、组件(COM)程序设计 有了面向对象程序设计方法,就彻底解决了代码重用的问题了吗?答案是:否!硬件越来越快,越来越小了,软件的规模却也越来越大了,集体合作越来越重要,代码重用又出现的新的问题。 我用C+写的类,不能被BASIC重用不能跨语言;你要干什么,想重用我的代码?不行,这样你就看见了我的设计思想只能在源程序级别重用,不能在二进制级别(可执行代码及)重用。 我耗尽毕生的精力,写了一个包罗万象的类库,但没有人用。因为他们说:你这个太大了,我的程序只有1K,你却给我一个 10000MB 的库MFC 的尴尬。 太好了,我终于找到了程序中的一个BUG,已经修改完成,而且是只改动了一个字节。接下来我要重新向我的用户分发新的版本,我的用户有.10万个升级的非鲁棒性,不是我分发累死了,就是用户重新安装累死了。(鲁棒:robust。意为强壮性的,平顺的,顺滑的.鬼知道是哪个不懂计算机的人翻译的这个词汇。) 我想写一个集大成的软件,这个软件的功能是我中有你,你中有我。既能实现文字编辑,又能实现电子表格计算,又能实现自动翻译,还能画图,还能实现数据库检索,还可以看电影.只要用了我的这个软件,想要什么就有什么,我要强占整个软件的市场-OLE实现的重用功能,只要学会了COM,这些都不是问题了。 用户甲要求我的软件窗口上下分割,用户乙要求我的软件窗口左右分割.我需要在我的软件基础上,派生出100个类型,可怎么办呀?将来怎么维护呀?-在脚本的支持下,实现同一程序的的灵活配置而重用,问题迎刃而解了。 我是个老板,你知道我有多痛苦吗?我手下的员工向我提出加工资的要求,我不得不答应呀。因为如果这个员工跳槽了,他的代码要维护起来有多难!现在好啦,我要求员工统统用组件写模块,想加工资?门都没有,威胁我要走?那你走吧,这个月的工资也不发了。反正用组件写的代码,我可以很容易地进行包容和聚合实现维护。(老板的福音,程序员的悲哀)。 还有好多那,现在想不起来了. COM程序设计方法,就是解决以上问题的一个方式。有很多朋友觉得COM非常复杂难懂,不想学习了。你一定学习过程序设计的最基本的方法(非结构化设计:汇编、gwBasic.),然后,你又学习了结构化程序设计(C、Pascal.),然后,你又努力学习并熟练掌握了面向对象的程序设计方法(C+、Delphi、Java.),那么不要怕,要有信心去学习组件程序设计,它只是一个设计方法和思想,并且是目前较高级的方法,如果不掌握,就太可惜了。 学习了结构化程序设计,你就会“藐视”那些不遵守结构化设计思想而写出的代码; 学习了面向对象设计,你就会“嘲笑”那些为找BUG而晕头转向的程序员; 同样,学习了组件程序设计,你就会站在更高的层次看待程序设计。七、结束语 写程序的目的是什么?养家糊口、兴趣使然、我的事业.这些都对。但我要强调的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 指南专业标准试题及答案
- 2026届安徽省合肥市普通高中学业水平选择性考试物理模拟检测试卷(五)
- 知识社群知识产业市场调查及技术创新
- 【高中语文】《论语》十二章+课件+统编版高二语文选择性必修上册
- 2024-2025学年山东省临沂市郯城县八年级(上)期末数学试卷(含答案)
- 建筑局部元素采集方案设计
- 加强校园维稳安全教育
- 山东省淄博市周村区第一中学2024-2025学年六年级下学期3月月考生物试题(含答案)
- 混凝土表面病害施工方案
- 虎年寒假实践活动方案策划
- 安全围栏检修方案
- 我的家乡课件中班
- 2024永磁外转子 EC 风机系统技术条件
- 2024年下载客运从业资格证模拟考试题库
- 高职高考英语词汇表
- 2024年华东电力设计院限公司校园招聘高频考题难、易错点模拟试题(共500题)附带答案详解
- 常住人口登记表(集体户口)-英文翻译
- 药品经营质量管理规范培训课件
- 《宠物护理与美容技术》课件-染色
- 法律检索教学课程设计
- 2024版个人居间协议书范本
评论
0/150
提交评论