1、1 1第11章 软 件 实 现第11章 软 件 实 现11.1 程序设计语言11.2 编码风格11.3 程序的效率本章小结习题2 2第11章 软 件 实 现11.1 程序设计语言程序设计语言一直在不断地演化和演变,其发展经历了从机器语言到高级语言的过程。计算机问世初期,程序设计语言是与计算机硬件紧密相关的机器语言和汇编语言,编写这种语言程序难度大、效率低,不易于理解且难以调试。3 3第11章 软 件 实 现11.1.1 程序设计语言的特性特定的程序设计语言有一些特定的限制,它们影响着程序员描述和处理问题的方式。程序设计语言应着重考虑程序员易学易用、不易出错,因此程序设计语言须考虑下列特性:(1
2、)一致性(Uniforminy)。(2)二义性(Ambiguity)。(3)紧致性(Compactness)。(4)局部性(Locality)。(5)线性(Linearity)。4 4第11章 软 件 实 现11.1.2 程序设计语言的选择总的来说,程序设计语言的选择需要结合具体问题进行分析评价,下面给出一些可供参考的实用标准:(1)系统用户的要求。(2)程序员的知识。(3)软件可移植性要求。(4)软件的应用领域。5 5第11章 软 件 实 现目前面向对象方法是软件开发的主流方法,因此面向对象语言的选择问题更受关注。开发人员在选择面向对象语言时,应该着重考虑以下一些实际因素:(1)将来能否占主
3、导地位。(2)可复用性。(3)类库和开发环境。(4)其他因素。6 6第11章 软 件 实 现11.2 编 码 风 格11.2.1 命名程序设计过程要涉及到对变量、常量、函数、类、对象等编程元素进行命名。一个变量的作用域越大,它的名字所携带的信息就应该越多。7 7第11章 软 件 实 现下面是一些通用的规则:(1)标识符的命名应当直观,可以望文知义,最好采用英文单词或其组合。(2)标识符的长度应当符合“最小长度下的最大信息”原则,过长的英文单词应该采用一些通用而合理的缩写或者应用领域专业术语的缩写。(3)程序中不要出现仅依靠大小写来区分的相似标识符。(4)程序中不要出现局部变量和全局变量同名的现
4、象,以免引起误解。(5)变量名应当使用“名词”或者“形容词+名词”的形式。(6)函数名应当使用“动词”或者“动词+名词”的形式。8 8第11章 软 件 实 现例11.1 Java命名实例。package org.jr.jzj.editor;import java.awt.*;import javax.swing.*;9 9第11章 软 件 实 现public class LineNumber extends JComponent private final static Color DEFAULT_BACKGROUND=Color.white;private final static Colo
5、r DEFAULT_FOREGROUND=new Color(153,153,204);private final static Color DEFAULT_LINECLR=new Color(192,192,192);private final static Font DEFAULT_FONT=new Font(SansSerif,Font.PLAIN,12);private final static int HEIGHT=Integer.MAX_VALUE-1000000;private final static int MARGIN=5;1010第11章 软 件 实 现 private
6、FontMetrics fontMetrics;private int lineHeight;private int currentRowWidth;private JComponent component;private int componentFontHeight;private int componentFontAscent;1111第11章 软 件 实 现 public LineNumber(JComponent component)if(component=null)setBackground(DEFAULT_BACKGROUND);setForeground(DEFAULT_FO
7、REGROUND);setFont(DEFAULT_FONT);ponent=this;1212第11章 软 件 实 现 else setBackground(DEFAULT_BACKGROUND);setForeground(component.getForeground();setFont(component.getFont();ponent=component;1313第11章 软 件 实 现 componentFontHeight=component.getFontMetrics(component.getFont().getHeight();componentFontAscent=c
8、omponent.getFontMetrics(component.getFont().getAscent();setPreferredWidth(9999);this.setBorder(BorderFactory.createLineBorder(DEFAULT_LINECLR,1);1414第11章 软 件 实 现 public void setPreferredWidth(int row)int width=fontMetrics.stringWidth(String.valueOf(row);if(currentRowWidth 0)this.lineHeight=lineHeigh
9、t;public int getStartOffset()return component.getInsets().top+componentFontAscent;1717第11章 软 件 实 现 public void paintComponent(Graphics g)int lineHeight=getLineHeight();int startOffset=getStartOffset();Rectangle drawHere=g.getClipBounds();g.setColor(getBackground();g.fillRect(drawHere.x,drawHere.y,dr
10、awHere.width,drawHere.height);g.setColor(getForeground();1818第11章 软 件 实 现 int startLineNumber=(drawHere.y/lineHeight)+1;int endLineNumber=startLineNumber+(drawHere.height/lineHeight);int start=(drawHere.y/lineHeight)*lineHeight+startOffset;for(int i=startLineNumber;i=endLineNumber;i+)String lineNumb
11、er=String.valueOf(i);int width=fontMetrics.stringWidth(lineNumber);1919第11章 软 件 实 现 g.drawString(lineNumber,MARGIN+currentRowWidth-width,start);start+=lineHeight;setPreferredWidth(endLineNumber);2020第11章 软 件 实 现11.2.2 注释注释是帮助阅读和理解程序的有效手段,用自然语言或伪码描述。注释说明了程序的功能,特别是在维护阶段,对理解程序提供了明确的指导。2121第11章 软 件 实 现书
12、写注释应该注意以下问题:(1)程序中的注释不宜过多,否则会使人眼花缭乱。(2)不必要注释含义已经十分清楚的代码。(3)修改代码时应该同时修改注释,以保证代码和注释的一致性。(4)注释应该准确易懂,防止出现二义性,错误的注释不但无益而且有害。(5)注释的位置应该与被描述的代码相邻,应该写在程序代码的上方并且和代码左对齐。(6)变量定义和分支语句必须写注释,因为这些语句往往是程序某一特定功能的关键。2222第11章 软 件 实 现例11.2 Java注释实例。package com.lowagie.text;import .MalformedURLException;/*A Watermark i
13、s a graphic element(GIF or JPEG)*that is shown on a certain position on each page.*seeElement*seeJpeg*seeGif*seePng*/2323第11章 软 件 实 现public class Watermark extends Image implements Element /member variables /*This is the offset in x-direction of the Watermark.*/private float offsetX=0;/*This is the
14、offset in y-direction of the Watermark.*/private float offsetY=0;2424第11章 软 件 实 现/Constructors /*Constructs a Watermark-object,using an Image.*paramimagean Image-object*paramoffsetXthe offset in x-direction*paramoffsetYthe offset in y-direction*/2525第11章 软 件 实 现 public Watermark(Image image,float of
15、fsetX,float offsetY)throws MalformedURLException super(image);this.offsetX=offsetX;this.offsetY=offsetY;/implementation of the Element interface 2626第11章 软 件 实 现/*Gets the type of the text element.*returna type*/public int type()return type;/methods to retrieve information 2727第11章 软 件 实 现/*Returns
16、the offset in x direction.*returnan offset*/public float offsetX()return offsetX;2828第11章 软 件 实 现/*Returns the offset in y direction.*returnan offset*/public float offsetY()return offsetY;2929第11章 软 件 实 现11.2.3 源代码版式1适当的空行在源代码中适度地使用空行可以使程序的结构更加清晰,空行分隔一般出现在:源文件中的各个节之间。源文件中的类与类、类与接口之间。方法定义之间。局部变量定义和第一
17、个语句之间。注释块或注释行之前。语句的不同逻辑分段之间。3030第11章 软 件 实 现2代码行及行内空格 一行代码只写一条语句,避免使用复杂的语句行。行内空格一般出现在关键字和括号之间、参数列表中逗号之后、二元运算符之间、语句中的表达式之间以及赋值符号之后。3131第11章 软 件 实 现例11.3 代码行风格实例及其修改结果。(1)变量定义。避免使用:int width,height;/宽度和高度应该采用:int width=10;int height=10;3232第11章 软 件 实 现(2)运算表达式。避免使用:x=a+b;a+=c+d;应该采用:x=a+b;a+=c+d;3333第
18、11章 软 件 实 现(3)其他语句。避免使用:prints(size is +foo+n);应该采用:prints(size is +foo+n);/增加了空格3434第11章 软 件 实 现3分行、对齐与缩进 代码行最大长度最好控制在70或80个字符之内,超过这个范围应当分成多行。分行应在逗号之后、操作符之前。长表达式应在低优先级的操作符处拆分成新行,操作符放在新行之首。拆分出的新行要进行适当的缩进,建议采用8个空格缩进,使排版整齐,语句可读。3535第11章 软 件 实 现例11.4 分行、对齐与缩进的实例。(1)函数参数列表分行。myMethod(longExpression1,lon
19、gExpression2,longExpression3,longExpression4,longExpression5);3636第11章 软 件 实 现(2)运算表达式分行。避免使用:longName1=longName2*(longName3+longName4-longName5)+4*longName6;应该采用:longName1=longName2*(longName3+longName4-longName5)+4*longName6;3737第11章 软 件 实 现(3)条件表达式分行。避免使用:if(condition1&condition2)|(condition3&con
20、dition4)|!(condition5&condition6)doSomethingAboutIt();3838第11章 软 件 实 现应该采用:if(condition1&condition2)|(condition3&condition4)/分行和缩进|!(condition5&condition6)doSomethingAboutIt();3939第11章 软 件 实 现也可以采用:if(condition1&condition2)|(condition3&condition4)|!(condition5&condition6)doSomethingAboutIt();4040第11
21、章 软 件 实 现11.2.4 异常处理在C+语言中,catch 语句可以捕获各种类型的异常。然而,在处理异常的过程中,人们往往会忘记对抛出的异常对象的内存释放。下面的程序使用了C+的异常处理机制,但是这个程序中存在一个错误,有可能造成内存泄漏。4141第11章 软 件 实 现class myExceptionpublic:int errorInfo;myException(int errorInfo)this-errorInfo=errorInfo;4242第11章 软 件 实 现void ShowException(int code)if(code“错误代玛为:”e-errorInfo;4
22、444第11章 软 件 实 现在C+语言中,异常处理机制类似于goto语句的执行原理。上述程序中新创建的异常对象指针作为参数被传进了catch函数,而catch函数可以看成是一个不再返回调用点的函数,因此当处理过程结束后,程序并没有释放这个新对象占用的空间,这样在某些关键性的应用中可能会导致系统内存不足。4545第11章 软 件 实 现为了避免上述问题的出现,应该在catch 块的最后加上delete语句,具体修改如下:catch(myException*e)cout“错误代玛为:”e-errorInfo;delete p;4646第11章 软 件 实 现异常处理机制相当有用,但同时也容易被误
23、用而导致代码结构混乱。在使用异常处理的过程中,必须十分清楚在没有异常抛出、异常抛出时和异常处理后的程序执行流程,把握异常处理中的跳转和嵌套机制,并注意异常处理时存在的问题。4747第11章 软 件 实 现11.3 程序的效率程序的效率是指程序执行速度的快慢和程序占用存储空间的大小。效率要求实际上就是性能要求,对效率追求应取决于在需求分析阶段所确定的效率方面的要求;好的设计也可以提高程序效率,即在设计阶段要设计出高效率的程序结构;编码阶段则是提高程序效率的最后时机。因此,编码过程中,在不影响程序的清晰性和可读性的前提下,必须考虑如何提高程序效率。下面从三个方面来讨论程序的效率问题。4848第11
24、章 软 件 实 现1程序运行时间设计阶段所确定的算法的效率决定了源程序的效率;在编码阶段,编码风格也影响着程序的执行速度和存储器要求。因此,在编码阶段,将设计的结果转化成程序代码时,应遵循以下原则:(1)编码前先化简算术表达式和逻辑表达式。(2)仔细检查算法中嵌套的循环,以确定是否有语句可以从内层向外移。(3)尽量不使用多维数组。(4)尽量不使用指针和复杂的表。(5)使用执行时间短的算术运算。4949第11章 软 件 实 现(6)在表达式中尽量避免出现不同的数据类型。(7)尽量使用整数算术表达式和布尔表达式。(8)选用等效的高效率算法。对某些效率起决定性因素的系统,尽量使用具有“优化”功能的编
25、译程序,以自动生成高效的目标代码。5050第11章 软 件 实 现下面程序中的lowerStr函数是将字符串中所有大写字母变成小写字母,而main函数中含有测量并显示运行时间的功能。其中一些语句会对程序的执行速度产生影响。#include#include#include#include 5151第11章 软 件 实 现void lowserStr(char*str)char*p;for(p=str;pstr+strlen(str);p+)if(isupper(*p)*p=tolower(*p)5252第11章 软 件 实 现int main()char msg255=Some of the L
26、ETTERS are Capitals.n;int count=0;printf(请输入执行次数);scanf(%d,&count);clock_t timeBegin,timeEnd;5353第11章 软 件 实 现timeBegin=clock();for(int i=0;icount;i+)lowerStr(msg);timeEnd=clock();printf(花费时间:%.3f秒n,(timeEnd-timeBegin)/CLOCKS_PER_SEC);return 0;5454第11章 软 件 实 现可以看出上述代码中存在问题,在lowerStr函数的for循环条件表达式中包含st
27、rLen函数,这种写法会造成每次循环时都要重复调用该函数,从而使程序的运行效率大大降低。由于在循环过程中,strLen函数的计算结果是不变的,因此可以提前将字符串的长度计算出来并赋给一个变量,然后在循环中使用该变量即可。5555第11章 软 件 实 现根据上面的分析,lowerStr函数的修改结果如下:void lowserStr(char*str)char*p;int len=strLen(str);for(p=str;pstr+len;p+)if(isupper(*p)*p=tolower(*p)5656第11章 软 件 实 现使用了优化方法后,让该程序循环执行10 000次,结果显示未优
28、化的程序执行时间为0.144s,优化后的执行时间为0.092s,速度大约提高36%。编写程序时,不要费力地追求编写快速的程序,应该努力编写好的程序。具备良好的编程习惯,程序效率会自然随之而来。如果发现编写的程序速度确实比较慢,可以在性能分析器的帮助下找到问题的根源,然后设法优化系统中相关的部分。5757第11章 软 件 实 现2存储器效率在微型计算机中,对软件设计和编码的最大制约是存储限制,即要求使用最少的存储单元。因此,要选用有紧缩存储器特性的编译程序,生成较短目标代码,在必要时可以使用汇编语言。5858第11章 软 件 实 现在大中型计算机系统中,对内存管理采取的是基于操作系统的分页功能的
29、虚拟存储管理。因此,存储限制不再是主要问题,不必强调所使用的空间越少越好,存储的效率直接与操作系统的分页功能有关。一般来说,使用能保持功能域结构化的控制结构,以减少页面调度,减少内外存之间的交换,是提高效率的好方法。提高程序执行效率的技术也能提高存储器效率。提高存储器效率的关键在于程序的简单性。5959第11章 软 件 实 现重复使用同一个对象,而不是每次需要的时候就创建一个功能等价的新对象,是一种常用的程序性能优化手段。请仔细分析下面的Java程序:String s1=new String(“Not Good Choice”);String s2=Good Choice;6060第11章 软
30、 件 实 现在上面的程序中,s1和s2使用了两种不同的手段进行定义,二者的执行效率会有很大差别。如果这两个语句都在一个循环中执行,s1的定义语句将产生多个String实例,而s2的定义语句由于利用了Java语言不变对象的特点,只会产生一个String对象实例。显然,s2语句的执行效率比s1语句要高。6161第11章 软 件 实 现3输入输出的效率从设计和编码的角度来看,应该考虑以下有关提高输入输出效率的规则:(1)输入输出的请求易于理解。(2)为减少通信的额外开销,所有输入输出都应有适当的缓冲。(3)对辅存(如磁盘)的访问应选择尽可能简单的方式。(4)对辅存的输入输出应以块为单位进行。(5)任
31、何不易理解的“超高效”的输入输出,则不能采用。(6)应考虑输入输出设备(如终端或打印机等)的特性,尽量改善其输入输出的质量和速度。(7)好的输入输出设计风格对提高输入输出效率会有明显的效果。6262第11章 软 件 实 现本 章 小 结本章从特性等方面介绍了程序设计语言,讨论了软件实现中程序设计语言的选择问题。并从对编程元素的命名、程序代码的注释和版式、代码中的异常处理和代码的性能等方面讨论了软件实现中的编码风格。从程序运行时间、存储器效率和输入输出的效率等方面讨论了程序的效率问题。6363第11章 软 件 实 现习 题1选择一种面向对象程序设计语言,总结其主要特性和特殊机制2列出三种常见的专
32、用程序设计语言,并说明它们的应用领域。3你认为是否应该强制采用软件编码规范?列出理由。6464第11章 软 件 实 现4请分析比较下面两个for循环的优缺点。第一个for循环:for(i=0;iN;i+)if(condition)doSomething();else doOtherthing();6565第11章 软 件 实 现第二个for循环:if(condition)for(i=0;iN;i+)doSomething();elsefor(i=0;iN;i+)doOtherthing();6666第11章 软 件 实 现5面向对象实现应该选用哪种程序设计语言?为什么?6软件实现的主要任务是什么?