﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Sung in Blog-文章分类-software Development</title><link>http://www.blogjava.net/qq13367612/category/3973.html</link><description>&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font class="subhead" size=3&gt;&lt;b&gt;一些技术文章 &amp; 一些生活杂碎&lt;/b&gt;&lt;/font&gt;</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 09:14:49 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 09:14:49 GMT</pubDate><ttl>60</ttl><item><title>windows环境CVS client使用入门</title><link>http://www.blogjava.net/qq13367612/articles/17125.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 00:48:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/17125.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17125.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/17125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17125.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17125.html</trackback:ping><description><![CDATA[<STRONG>CVS client使用前必须明白的基本概念<BR><EM>1,CVS</EM></STRONG><BR>CVS (Cuncurrent Versions System)是基于TCP/IP协议的版本控制工具,也是Open source界最重要的开发工具之一。 <BR>它原来主要工作在UNIX平台下，现在在windows平台上也出现了很多GUI工具。 <BR>关于其由来，发展，请参阅http://www.redsaga.com/CVS_newbie_win32/www.cvshome.org 。 <BR>CVS和其他大部分版本控制软件一样，是Client/Server结构的。你必须在本地为CVS repository准备好一个目录，来和服务器同步源代码。 <BR>和在Windows 开发平台中拥有很大用户群的Visual Source Safe(VSS)相比,CVS主要由两个不同之处。 <BR>一是VSS依靠服务器上的一个共享目录提供服务，每一个client必须能够访问这个共享目录。这也就决定了source safe在TCP/IP环境下使用很困难。对于分布跨越数个城市甚至国家的工作小组来说，只有通过VPN才能够安全的访问source safe数据库。(SourceGear公司(www.sourcegear.com)发售名为SourceOffSite的商业软件包,US$239 per user，也可以为远程用户提供通过TCP/IP访问source safe数据的能力。) 而CVS依靠TCP/IP连接提供服务，所以它天生就是为了在internet上协同工作而设计的。虽然基本的pserver连接安全性不是很高，但是通过使用SSH,可以获得很高的安全性。 <BR>二是CVS反对对文件上锁的机制。VSS以及其他很多传统版本控制工具要求一个文件只能有一个使用者，它必须先checkout声明编辑文件的独享权力，直到checkin为止。但是对于地理上不限制使用者位置的CVS来说，等待一个用户checkin是一件痛苦的事情，而互相沟通比一个紧密工作的团体更困难。CVS采取多个用户可以同时对一个文件进行编辑，然后commit的方式解决这个问题。假设由于沟通不足出现冲突，使用者必须手工解决冲突之后再进行commit。在这种情况下，冲突的开发者必须努力进行足够的沟通以避免再次冲突。 <BR>CVS服务器可以被配置为把commit记录自动张贴到新闻组或者开发者的email信箱中去。注意这些辅助记录将有助于开发者之间的合作。 <BR><STRONG><EM>2, repository,module</EM></STRONG><BR>CVS服务器上，一个源代码仓库被称为一个repository,一个server上通常可以运行多个repository,每个repository都是完全独立的，可以有不同的用户列表和访问规则。在一个repository之下，文件按照module组织，每一个module就相当于一个工程,大致上相当于Source safe里面的project。 <BR>VSS在你连接上服务器之后，会列出所有的project。但并不是所有的CVS server都会提供module的列表。事实上，哪些module被公开是由管理员控制的。如果你知道一个被隐藏的module的名字，你仍然可以正常的访问这个module。 <BR><STRONG><EM>3,CVSROOT</EM></STRONG><BR>CVS依靠运行在服务器上的一个服务程序提供TCP/IP的连接。为了访问一个CVS数据库，你必须知道你所使用的协议，服务器的地址，服务器提供的Repository的名称以及你的用户名和密码。 <BR>有数种协议可供选择。Unix/Linux机器上的CVS通常使用pserver协议，这是一种不非常安全的协议，但是如果你有额外的安全要求，可以通过SSH进行增强。除此之外，NT机器还支持ntserver协议，它通过主机的NT用户表进行访问控制(但是这是在internet上不可用的方法）。kserver和gserver协议用的比较少，他们依据Kerboses提供额外的安全保护。 <BR>你有必要知道CVSROOT这个参数。CVSROOT是一个用":"开始及分隔各个部分的字符串，它包含了协议、用户名、服务器地址和repository名称。对于用户来说，CVSROOT就像URL一样，是访问一个server的途径。 <BR>一个典型的CVSROOT=:perser:cao@61.155.107.187:/cvs。这里,pserver是协议名称，cao是用户id，61.155.107.187是主机ip,/cvs是repository的名字。NT主机的repository一般会采取d:/CVSROOT之类的格式。 <BR>另一个例子是:pserver:anonymous@jivesoftware.com:/cvs,这是jivesoftware公司提供的开放源代码java技术论坛的CVSROOT。 <BR>在windows下使用命令行方式，这个参数可以通过一个环境变量使用。在windows 2000/XP系统中，你可以通过在'My computer'的properties中选择advanced,然后选择'Enviroment Variables'来输入这个环境变量。 <BR><STRONG><EM>4，checkout,update</EM></STRONG><BR>为了得到module下面的源代码，你只需要使用checkout指令。和Visual source safe不一样，checkout只是取得文件，而非锁文件。 <BR>如果你已经有了本地文件，为了和server保持同步，你需要进行update操作。update会自动把server上的新内容取到本机来，如果你本地文件进行过了改动，它会帮您做合并工作。 <BR>checkout 和 update既可以针对一个特定的文件，也可以针对一个目录或者整个module。 <BR><STRONG><EM>5, commit</EM></STRONG><BR>如果你对本地代码做了任何修改，或者增加一个文件，删除一个文件，每当你需要把你的改变提交到server上的时候，你就需要做commit动作。假设两个人都在本地修改了同一个文件，那么他们就像在进行一个竞赛，如果你快，那么你赢了。后commit的人将被server拒绝，不得不合并你的修改再次提交。 <BR>commit既可以针对一个特定的文件，也可以针对一个目录或者整个module。 <BR><STRONG><EM>6, revision</EM></STRONG><BR>Revision是指每一个文件的版本信息。当你第一次增加一个文件到repository的时候，它会有一个初始revision是1.1,以后每次提交，就会增加到1.2,1.3... <BR>在一个branch中的文件，有相对于这个branch的版本号。如果你对文件作了tag,那么你会看到revision变成1.1.1.1的形式。具体的含义我们在branch和tag的时候描述。 <BR><STRONG><EM>7，branch</EM></STRONG><BR>Branch是一棵正常生长的代码树中的枝杈。开始的时候，任何一个module都有一个主枝被称为'HEAD'。 <BR>一个branch最终要么被合并到主干中去，要么被结束。branch通常用来debug,如果这个bug被fix了，修改bug的代码应该被合并到主枝上去。一个branch也可能经历多次与主枝的合并。 <BR><STRONG><EM>8, tag</EM></STRONG><BR>Tag用来进行标示必要的信息。当您进行一次公开发布之前，您有必要对主枝标示"release 1.0"。这样您以后就可以随时回到这个版本。 <BR>//to do: 请完善这里的描述<BR><BR><STRONG>CVS命令行</STRONG><BR>在得到CVSROOT和你的口令之后，你就可以试着登陆了。 <BR>首先，由于其他所有的GUI工具都是基于CVS基本协议的，而且他们可能会提供CVS的命令行或者等价形式作为显示的一部分，所以你应该对命令行操作有所了解。如果你还没有一个cvs。exe的命令行程序，从http://www.redsaga.com/CVS_newbie_win32/www.cvsnt.org你可以得到一个cvsnt的下载连接，其中就包含了一个命令行的cvs.exe程序。我们先从它开始（为了作为一个client使用，你不需要安装cvsnt的server组件)。CVSNT的cvs.exe是专门为windows编写的，你需要把cvs.exe放在你的path里面。 <BR><STRONG><EM>1.进入命令行方式。</EM></STRONG><BR>和VSS一样，你也需要在本地有一个工作目录对应于一个repository。假设这个目录是'd:\works\sandbox'。请切换到这个目录。<BR>输入"cvs"。你会看到: <BR><IMG height=252 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-1.jpg" width=642> <BR>这些提示信息告诉您关于cvs的基本语法。cvs后面跟着的是全局参数，然后是命令，最后是命令的参数。<BR><EM><STRONG>2.login </STRONG></EM><BR>正确的login不会有任何输出，否则会告诉你错误原因。<BR>cvs <BR><IMG height=47 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-2.jpg" width=640> <BR><EM><STRONG>3.下面我们看看这个CVS server中有哪些module。 </STRONG></EM><BR><IMG height=75 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-3.jpg" width=640> <BR><STRONG><EM>4.假设现在我们工作的项目是projectX,下面我们需要得到它下面的全部文件。<BR></EM></STRONG><IMG height=179 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-4.jpg" width=641><BR>现在让我们看一下我们得到了什么。 <STRONG><EM><IMG height=232 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-5.jpg" width=643></EM></STRONG><BR>在d:/works/sandbox目录下，你可以看到有一个projectX目录。这就是你得到的所有文件。 <BR>这个目录下你会发现一个叫做 CVS的目录。危险！请不要删除这个目录，或者改名，或者改动其中的任何文件，除非你知道你在做什么。这个目录是CVS的控制目录。如果你用过source safe,你一定很熟悉。scc这个文件，CVS目录的作用就和这个控制文件一样，都是用来记录你访问服务器的参数。 <BR>这里我们需要解释一下cvs和VSS的名词差别。在VSS中，checkout意味着你将获得一个文件的修改权，而cvs中checkout的这个含义取消了，仅仅指取得文件的新版本。很多cvs server会有一个anonymous用户，他只有checkout权限，也就意味着它只读。 <BR><BR><EM><STRONG>5.让我们试着加入一个文件:</STRONG></EM><IMG height=90 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-6.jpg" width=640><BR>在d:/works/sandbox/projectX下，新建一个文件newfile.txt, <BR>然后，在这个目录下执行: <BR><IMG height=62 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-7.jpg" width=640><BR>你需要commit它才能被sever接受。<BR><IMG height=20 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-8.jpg" width=638> <BR>一个notepad窗口弹出请您输入注释。 <BR><IMG height=276 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-9.jpg" width=641> <BR>这是commit完成的结果。现在的版本号是1.1。<BR><IMG height=114 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-10.jpg" width=640> <BR><STRONG><EM>6.好了，现在假设您需要改一下这个文件的内容。<BR><IMG height=93 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-11.jpg" width=639> </EM></STRONG><BR>CVS可以帮助您比较现在您的版本和repository中的版本有什么不同。<BR><IMG height=193 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-12.jpg" width=638> <BR>好了，现在您可以提交您的新文件。 <BR><IMG height=100 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-13.jpg" width=635> <BR>CVS会帮您保留您的各个版本。在commit之后，现在我们来看一看各个版本的history。 <BR><IMG height=340 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-14.jpg" width=630> <BR><STRONG><EM>7.最后，为了完成这个试验，请把这个newfile文件删去。 </EM></STRONG><BR><IMG height=185 src="http://51cmm.csai.cn/casepanel/CM/Images/No063-15.jpg" width=636><BR>我们现在认识了一些最基本的CVS入门级指令。 <BR>其实CVS是非常强大的，我们并没有用到一些更复杂的功能，请参阅cvs的手册来得到更为详尽的帮助。 <BR>在多人同时开发的时候，冲突有时候也是不能避免的。 <BR>// to do:加入处理 cvs update和处理merge的部分。 <BR>更多的资料请参阅Karl Fogel的《Open Source Development With CVS》一书。该书的中文译本《CVS开源软件开发技术》由机械工业出版社出版(ISBN 7-111-08891-3/TP.1885,人民币35.00)。按此连接到华储网上电脑书店订购http://www.huachu.com.cn/asp/book/brow.asp?lbbh=B99112091 <BR><STRONG>用MFC编写的windows平台CVS图形界面 --- WinCVS(未完成)</STRONG><BR>好了，就像黑白电影总是会过渡到彩色电影一样（正巧，就像现在发生在移动电话市场的情况一样），cvs命令行之外，一些图形界面的程序被编写出来简化你的操作。由于CVS的协议是公开的，所以，只要你的编程语言支持socket通讯，你也可以写一个。这个世界上有好几个这样的尝试，WinCVS就是其中一个。它是在win32平台上用MFC开发的。从我自己的体会来看，这是一个有益的尝试，但是还远远称不上精美。和VSS和source off site，borland的tram source，以及PVCS的界面比较，这个windows界面还显得很粗糙，差强人意。 <BR>但是我们还是可以从它开始进行我们的工作。 <BR>下载请至http://www.redsaga.com/CVS_newbie_win32/www.cvsgui.org,那里有最新的版本。 <BR>打开开始菜单的之后，你看到的是这样一个界面： <BR><BR>界面由3个主要区域组成，坐上角是目录结构，右上角是文件夹详细内容，下方是CVS命令的log。你可以从下方的log中得知cvs运行的详细信息。 <BR><BR>首先你要做的事情就是告诉winCVS你的CVSROOT。 <BR><BR>这个对话框在winCVS1。2和1。3的各个版本中稍有不同，但是不妨碍你输入CVS服务器的详细信息。 <BR><BR>下一步就是login。 <BR><BR>好了，现在我们就可以去得到一份jive2的源代码了。 <BR><BR>你可以看到，winCVS默认会加上压缩的参数，这样可以减少网络流量，加快速度。 <BR><BR>同样，用winCVS进行文件修改也是挺方便的。 <BR><BR>以上就是winCVS的简要操作指南。更进一步的复杂操作，请参阅其帮助。 <BR><BR>评价：值得关注，但不推荐。 <BR><STRONG>在windows Explorer里直接checkout ---- TortoiseCVS</STRONG><BR>TortoiseCVS已经是一个相当成熟的cvs界面了，它的工作方式也是非常有趣的。 <BR>TortoiseCVS是一个简洁有效的CVS界面。你完全可以用它替代winCVS做日常绝大多数的工作。他独特的运行方式非常迷人，在一些商用配置管理工具，比如Rational ClearCase中，你也可以看到这样的windows explorer扩展程序的工作方式。 <BR>缺点： <BR>1,tortoiseCVS会一直不释放它所操作的一些CVS文件和文件夹的系统资源。你可能会在试图删除一个在CVS控制之下的目录时遇到麻烦。windows系统认为有另一个进程正在打开它（显然就是TortoiseCVS)，我没有找到如何杀掉在后台执行的TortoiseCVS的方法。在这种情况，用TortoiseCVS去update一下另一个module有所帮助。或者请重起你的explorer。 <BR>2,如果你需要在两个不同的server或者repository上做checkout的工作，TortoiseCVS有一个bug。它会在你输入CVSROOT信息，checkout第二个module的时候仍然使用第一个repository的CVSROOT。但是再做一次同样的操作就会成功的达到你的目的。 <BR>评价：推荐。 <BR>按此进入关于使用TortoiseCVS的详细说明 <BR><STRONG>基于Netbeans Java类库的漂亮GUI ---- SmartCVS</STRONG><BR>使用java编写的CVS GUI在过去一两年中得到了长足的发展。拜jdk1.3所托，现在java程序的执行速度也有很大提高。Netbeans是由sun赞助的一个开放IDE,它其中包含了了CVS的基础类库。一个商业性的GUI,SmartCVS,就是在netbeans的稳定的cvs核心之上编写的。 虽然SmartCVS的商业版本收费,但是其基础版本是完全免费的,而这个基础版本已经足够大多数应用所需。 <BR><BR>SmartCVS你可以在www.regnis.de下载。 <BR>SmartCVS自从1.2beta版本开始引入smartCommit,不再区分add和checkin,统一使用commit进行操作。 <BR><BR>利用SmartCVS,你也可以看到一个文件的历史,以及图形化表示的各个分支。 <BR><BR>总结:SmartCVS正如他的名字所说的,是一个相当聪明,精致,漂亮的CVS 客户端。作为日常工作它是完全可以胜任的。如果和TortoiesCVS结合起来,可以给你相当理想的CVS工作平台。 <BR>同时,它也可以在所有jdk环境中运行,不仅仅局限于windows平台。 <BR><BR><STRONG>IDE集成</STRONG><BR>对程序员来说,真正在编码的时候,IDE才是每天接触最多的环境。如果能够在熟悉的IDE中使用CVS,当然实用性增强很多。Visual source safe 在Microsoft的开发工具产品VB,VC中集成得相当出色,也大大提高了使用使用Microsoft开发平台的工作效率。 <BR>下面我们就来看看一下在流行的java开发工具中如何使用CVS。 <BR><BR><STRONG>IBM Visual age for java IDE集成 插件 --- vajcvs (未完成)</STRONG><BR>Visual age for java支持一个开放的Tool API,CVSin就是这样的一个tool。 <BR>下载地址: <BR><BR>安装参见其内部readme文件。 <BR><BR><STRONG>Borland jBuilder 内置CVS支持</STRONG><BR>jBuilder很早就在3.0中内置CVS支持,而且当时cvs是jbuilder唯一一个正式支持的源代码管理工具。在最新的Builder 6中,当然对CVS也支持得很好。 <BR><BR><STRONG>完善的设计，领先的强大工作平台 --- Eclipse</STRONG><BR>及内置CVS支持IBM新的开放式集成开发环境,将是IBM visual age 系列开发工具的下一个平台。它提供CVS作为基础集成的一部分。在Eclipse中访问CVS是一件相当轻松自如的事情，Eclipse按照自己的需要封装了CVS,你可以看到，和其他访问方式完全不同，Eclipse是到现在为止最强大的CVS工作平台。<img src ="http://www.blogjava.net/qq13367612/aggbug/17125.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 08:48 <a href="http://www.blogjava.net/qq13367612/articles/17125.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LDAP介绍</title><link>http://www.blogjava.net/qq13367612/articles/16876.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 07:46:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16876.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16876.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16876.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16876.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16876.html</trackback:ping><description><![CDATA[<P><SPAN style="FONT-FAMILY: 宋体; Times: ">如果你在计算机行业工作，那么对</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">可能早有耳闻了。想深入地了解</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">吗？那么可以好好地读一下这篇文章。这篇介绍性的文章是一系列介绍如何在企业中设计、实现和集成</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">环境的文章的头一篇。主要是先让你熟悉一下</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的基本概念，那些比较困难的细节问题将放到以后讨论。在这篇文章中我们将要介绍：</SPAN></P>
<P><EM><STRONG><SPAN style="FONT-FAMILY: 宋体; Times: ">什么是LDAP?</SPAN></STRONG></EM></P>
<P><EM><STRONG><SPAN style="FONT-FAMILY: 宋体; Times: ">什么时候该用LDAP存储数据？</SPAN></STRONG></EM></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: "><STRONG><EM>LDAP目录树的结构</EM></STRONG></SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">现在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">技术不仅发展得很快而且也是激动人心的。在企业范围内实现</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">可以让运行在几乎所有计算机平台上的所有的应用程序从</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录中获取信息。</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录中可以存储各种类型的数据：电子邮件地址、邮件路由信息、人力资源数据、公用密匙、联系人列表，等等。通过把</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录作为系统集成中的一个重要环节，可以简化员工在企业内部查询信息的步骤，甚至连主要的数据源都可以放在任何地方。如果</SPAN><SPAN lang=EN-US>Oracle</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">、</SPAN><SPAN lang=EN-US>Sybase</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">、</SPAN><SPAN lang=EN-US>Informix</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">或</SPAN><SPAN lang=EN-US>Microsoft SQL</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">数据库中已经存储了类似的数据，那么</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">和这些数据库到底有什么不同呢？是什么让它更具优势？请继续读下去吧！</SPAN></P>
<H2><A name=_什么是LDAP?></A><SPAN style="FONT-FAMILY: 黑体">什么是</SPAN><SPAN lang=EN-US>LDAP?</SPAN></H2>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的英文全称是</SPAN><SPAN lang=EN-US style="COLOR: black">Lightweight Directory Access Protocol</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">，一般都简称为</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="COLOR: black; FONT-FAMILY: 宋体">。它是基于</SPAN><SPAN lang=EN-US style="COLOR: black">X.500</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">标准的，但是简单多了并且可以根据需要定制。与</SPAN><SPAN lang=EN-US style="COLOR: black">X.500</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">不同，</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="COLOR: black; FONT-FAMILY: 宋体">支持</SPAN><SPAN lang=EN-US style="COLOR: black">TCP/IP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">，这对访问</SPAN><SPAN lang=EN-US style="COLOR: black">Internet</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">是必须的。</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">的核心规范在</SPAN><SPAN lang=EN-US style="COLOR: black">RFC</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">中都有定义，所有与</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">相关的</SPAN><SPAN lang=EN-US style="COLOR: black">RFC</SPAN><SPAN style="COLOR: black; FONT-FAMILY: 宋体">都可以在</SPAN><SPAN lang=EN-US style="COLOR: black"><A href="http://www.ldapman.org/ldap_rfcs.html">LDAPman RFC</A></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">网页中找到。</SPAN></P>
<H3><SPAN style="FONT-FAMILY: 宋体; Times: ">怎么使用</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">这个术语呢？</SPAN></H3>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">在日常交谈中，你可能会听到有些人这么说：“我们要把那些东西存在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中吗？”，或者“从</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">数据库中取出那些数据！”，又或者“我们怎么把</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">和关系型数据库集成在一起？”。严格地说，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">根本不是数据库而是用来访问存储在信息目录（也就是</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录）中的信息的<B>协议</B>。更为确切和正式的说法应该是象这样的：“通过使用</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，可以在信息目录的正确位置读取（或存储）数据”。但是，也没有必要吹毛求疵，尽管表达得不够准确，我们也都知道对方在说什么。</SPAN></P>
<H3><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录是数据库吗？</SPAN></H3>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">就象</SPAN><SPAN lang=EN-US>Sybase</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">、</SPAN><SPAN lang=EN-US>Oracle</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">、</SPAN><SPAN lang=EN-US>Informix</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">或</SPAN><SPAN lang=EN-US>Microsoft</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的数据库管理系统（</SPAN><SPAN lang=EN-US>DBMS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）是用于处理查询和更新关系型数据库那样，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器也是用来处理查询和更新</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录的。换句话来说</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录也是<B>一种类型</B>的数据库，但是不是关系型数据库。不象被设计成每分钟需要处理成百上千条数据变化的数据库，例如：在电子商务中经常用到的在线交易处理（</SPAN><SPAN lang=EN-US>OLTP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）系统，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">主要是优化数据读取的性能。</SPAN></P>
<H3><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录的优势</SPAN></H3>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">现在该说说</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录到底有些什么优势了。现在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的流行是很多因数共同作用的结果。我在这里说的不过是一些基本的原因，请你注意一下这不过是一小部分原因。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">可能</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">最大的优势是：可以在任何计算机平台上，用很容易获得的而且数目不断增加的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的客户端程序访问</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录。而且也很容易定制应用程序为它加上</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的支持。</SPAN></P>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">协议是跨平台的和标准的协议，因此应用程序就不用为</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录放在什么样的服务器上操心了。实际上，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">得到了业界的广泛认可，因为它是</SPAN><SPAN lang=EN-US>Internet</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的标准。产商都很愿意在产品中加入对</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的支持，因为他们根本不用考虑另一端（客户端或服务端）是怎么样的。</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器可以是任何一个开发源代码或商用的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录服务器（或者还可能是具有</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">界面的关系型数据库），因为可以用同样的协议、客户端连接软件包和查询命令与</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器进行交互。与</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">不同的是，如果软件产商想在软件产品中集成对</SPAN><SPAN lang=EN-US>DBMS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的支持，那么通常都要对每一个数据库服务器单独定制。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">不象很多商用的关系型数据库，你不必为</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的每一个客户端连接或许可协议付费。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">大多数的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器安装起来很简单，也容易维护和优化。</SPAN></P>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器可以用“推”或“拉”的方法复制部分或全部数据，例如：可以把数据“推”到远程的办公室，以增加数据的安全性。复制技术是内置在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器中的而且很容易配置。如果要在</SPAN><SPAN lang=EN-US>DBMS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中使用相同的复制功能，数据库产商就会要你支付额外的费用，而且也很难管理。</SPAN></P>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">允许你根据需要使用</SPAN><SPAN lang=EN-US>ACI</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（一般都称为</SPAN><SPAN lang=EN-US>ACL</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">或者访问控制列表）控制对数据读和写的权限。例如，设备管理员可以有权改变员工的工作地点和办公室号码，但是不允许改变记录中其它的域。</SPAN><SPAN lang=EN-US>ACI</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">可以根据谁访问数据、访问什么数据、数据存在什么地方以及其它对数据进行访问控制。因为这些都是由</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录服务器完成的，所以不用担心在客户端的应用程序上是否要进行安全检查。</SPAN></P>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">对于这样存储这样的信息最为有用，也就是数据需要从不同的地点读取，但是不需要经常更新。例如，这些信息存储在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录中是十分有效的：</SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">公司员工的电话号码簿和组织结构图</SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">客户的联系信息</SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">计算机管理需要的信息，包括</SPAN><SPAN lang=EN-US>NIS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">映射、</SPAN><SPAN lang=EN-US>email</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">假名，等等</SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">软件包的配置信息</SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">公用证书和安全密匙</SPAN></P>
<H2><A name=_什么时候该用LDAP存储数据></A><A name=_什么时候该用LDAP存储数据？></A><SPAN style="FONT-FAMILY: 黑体">什么时候该用</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 黑体">存储数据？</SPAN></H2>
<P><SPAN style="COLOR: black; FONT-FAMILY: 宋体">大多数的</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">服务器都为读密集型的操作进行专门的优化。因此，当从</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="COLOR: black; FONT-FAMILY: 宋体">服务器中读取数据的时候会比从专门为</SPAN><SPAN lang=EN-US style="COLOR: black">OLTP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">优化的关系型数据库中读取数据快一个数量级。也是因为专门为读的性能进行优化，大多数的</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="COLOR: black; FONT-FAMILY: 宋体">目录服务器并不适合存储需要需要经常改变的数据。例如，用</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">服务器来存储电话号码是一个很好的选择，但是它不能作为电子商务站点的数据库服务器。</SPAN><SPAN lang=EN-US style="COLOR: black"></SPAN></P>
<P><SPAN style="COLOR: black; FONT-FAMILY: 宋体">如果下面每一个问题的答案都是“是”，那么把数据存在</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="COLOR: black; FONT-FAMILY: 宋体">中就是一个好主意。</SPAN><SPAN lang=EN-US style="COLOR: black"></SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">需要在任何平台上都能读取数据吗？</SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">每一个单独的记录项是不是每一天都只有很少的改变？</SPAN></P>
<P class=MsoListBullet><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">可以把数据存在平面数据库（</SPAN><SPAN lang=EN-US>flat database</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）而不是关系型数据库中吗？换句话来说，也就是不管什么范式不范式的，把所有东西都存在一个记录中（差不多只要满足第一范式）。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">最后一个问题可能会唬住一些人，其实用平面数据库去存储一些关系型的数据也是很一般的。例如，一条公司员工的记录就可以包含经理的登录名。用</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">来存储这类信息是很方便的。一个简单的判断方法：如果可以把保数据存在一张张的卡片里，就可以很容易地把它存在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录里。</SPAN></P>
<H2><A name=_LDAP目录树的结构></A><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 黑体">目录树的结构</SPAN></H2>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录以树状的层次结构来存储数据。如果你对自顶向下的</SPAN><SPAN lang=EN-US>DNS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">树或</SPAN><SPAN lang=EN-US>UNIX</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">文件的目录树比较熟悉，也就很容易掌握</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录树这个概念了。就象</SPAN><SPAN lang=EN-US>DNS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的主机名那样，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录记录的标识名（</SPAN><SPAN lang=EN-US>Distinguished Name</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，简称</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）是用来读取单个记录，以及回溯到树的顶部。后面会做详细地介绍。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">为什么要用层次结构来组织数据呢？原因是多方面的。下面是可能遇到的一些情况：</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">如果你想把所有的美国客户的联系信息都“推”到位于到西雅图办公室（负责营销）的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器上，但是你不想把公司的资产管理信息“推”到那里。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">你可能想根据目录树的结构给予不同的员工组不同的权限。在下面的例子里，资产管理组对“</SPAN><SPAN lang=EN-US>asset-mgmt</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”部分有完全的访问权限，但是不能访问其它地方。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">把</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">存储和复制功能结合起来，可以定制目录树的结构以降低对</SPAN><SPAN lang=EN-US>WAN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">带宽的要求。位于西雅图的营销办公室需要每分钟更新的美国销售状况的信息，但是欧洲的销售情况就只要每小时更新一次就行了。</SPAN></P>
<H3><SPAN style="FONT-FAMILY: 宋体; Times: ">刨根问底：基准</SPAN><SPAN lang=EN-US>DN</SPAN></H3>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录树的最顶部就是根，也就是所谓的“基准</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”。基准</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">通常使用下面列出的三种格式之一。假定我在名为</SPAN><SPAN lang=EN-US>FooBar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的电子商务公司工作，这家公司在</SPAN><SPAN lang=EN-US>Internet</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">上的名字是</SPAN><SPAN lang=EN-US>foobar.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN></P>
<P class=a><B><SPAN lang=EN-US>o="FooBar, Inc.", c=US </SPAN></B></P>
<P><I><SPAN style="COLOR: black; FONT-FAMILY: 宋体">（以</SPAN><SPAN lang=EN-US style="COLOR: black">X.500</SPAN></I><I><SPAN style="FONT-FAMILY: 宋体; Times: black">格式表示的基准</SPAN><SPAN lang=EN-US style="COLOR: black">DN</SPAN></I><I><SPAN style="COLOR: black; FONT-FAMILY: 宋体">）</SPAN><SPAN lang=EN-US style="COLOR: black"></SPAN></I></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">在这个例子中，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">o=FooBar, Inc. </SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">表示组织名，在这里就是公司名的同义词。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">c=US </SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">表示公司的总部在美国。以前，一般都用这种方式来表示基准</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。但是事物总是在不断变化的，现在所有的公司都已经（或计划）上</SPAN><SPAN lang=EN-US>Internet</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">上。随着</SPAN><SPAN lang=EN-US>Internet</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的全球化，在基准</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中使用国家代码很容易让人产生混淆。现在，</SPAN><SPAN lang=EN-US>X.500</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">格式发展成下面列出的两种格式。</SPAN></P>
<P class=a><B><SPAN lang=EN-US>o=foobar.com</SPAN></B></P>
<P><I><SPAN style="FONT-FAMILY: 宋体; Times: ">（用公司的</SPAN><SPAN lang=EN-US>Internet</SPAN></I><I><SPAN style="FONT-FAMILY: 宋体; Times: ">地址表示的基准</SPAN><SPAN lang=EN-US>DN</SPAN></I><I><SPAN style="FONT-FAMILY: 宋体; Times: ">）</SPAN><SPAN lang=EN-US></SPAN></I></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">这种格式很直观，用公司的域名作为基准</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。这也是现在最常用的格式。</SPAN></P>
<P class=a><B><SPAN lang=EN-US>dc=foobar, dc=com</SPAN></B></P>
<P><I><SPAN style="FONT-FAMILY: 宋体; Times: ">（用</SPAN><SPAN lang=EN-US>DNS</SPAN></I><I><SPAN style="FONT-FAMILY: 宋体; Times: ">域名的不同部分组成的基准</SPAN><SPAN lang=EN-US>DN</SPAN></I><I><SPAN style="FONT-FAMILY: 宋体; Times: ">）</SPAN><SPAN lang=EN-US></SPAN></I></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">就象上面那一种格式，这种格式也是以</SPAN><SPAN lang=EN-US>DNS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">域名为基础的，但是上面那种格式不改变域名（也就更易读），而这种格式把域名：</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">foobar.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">分成两部分</SPAN> <SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">dc=foobar, dc=com</SPAN><SPAN style="FONT-SIZE: 10.5pt; FONT-FAMILY: 黑体; Courier: black">。</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">在理论上，这种格式可能会更灵活一点，但是对于最终用户来说也更难记忆一点。考虑一下</SPAN><SPAN lang=EN-US>foobar.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">这个例子。当</SPAN><SPAN lang=EN-US>foobar.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">和</SPAN><SPAN lang=EN-US>gizmo.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">合并之后，可以简单的把“</SPAN><SPAN lang=EN-US>dc=com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”当作基准</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。把新的记录放到已经存在的</SPAN><SPAN lang=EN-US>dc=gizmo, dc=com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录下，这样就简化了很多工作（当然，如果</SPAN><SPAN lang=EN-US>foobar.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">和</SPAN><SPAN lang=EN-US>wocket.edu</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">合并，这个方法就不能用了）。如果</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器是新安装的，我建议你使用这种格式。再请注意一下，如果你打算使用活动目录（</SPAN><SPAN lang=EN-US>Actrive Directory</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">），</SPAN><SPAN lang=EN-US>Microsoft</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">已经限制你必须使用这种格式。</SPAN></P>
<H3><SPAN style="FONT-FAMILY: 宋体; Times: ">更上一层楼：在目录树中怎么组织数据</SPAN></H3>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">在</SPAN><SPAN lang=EN-US>UNIX</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">文件系统中，最顶层是根目录（</SPAN><SPAN lang=EN-US>root</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）。在根目录的下面有很多的文件和目录。象上面介绍的那样，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录也是用同样的方法组织起来的。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">在根目录下，要把数据从逻辑上区分开。因为历史上（</SPAN><SPAN lang=EN-US>X.500</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）的原因，大多数</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录用</SPAN><SPAN lang=EN-US>OU</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">从逻辑上把数据分开来。</SPAN><SPAN lang=EN-US>OU</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">表示“</SPAN><SPAN lang=EN-US>Organization Unit</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”，在</SPAN><SPAN lang=EN-US>X.500</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">协议中是用来表示公司内部的机构：销售部、财务部，等等。现在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">还保留</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">ou=</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">这样的命名规则，但是扩展了分类的范围，可以分类为：</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">ou=people, ou=groups, ou=devices</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">，等等。更低一级的</SPAN><SPAN lang=EN-US style="COLOR: black">OU</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: black">有时用来做更细的归类。例如：</SPAN><SPAN lang=EN-US style="COLOR: black">LDAP</SPAN><SPAN style="COLOR: black; FONT-FAMILY: 宋体">目录树（不包括单独的记录）可能会是这样的：</SPAN><SPAN lang=EN-US style="COLOR: black"></SPAN></P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>dc=foobar, dc=com </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=customers </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=asia </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=europe </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=usa </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=employees </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=rooms </P>
<P class=a><SPAN>&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ou=groups </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=assets-mgmt </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=nisgroups </P>
<P class=a><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ou=recipes<BR><BR></P>
<P><EM><STRONG><SPAN style="FONT-FAMILY: 宋体; Times: ">单独的LDAP记录</SPAN></STRONG></EM></P>
<P><EM><STRONG><SPAN style="FONT-FAMILY: 宋体; Times: ">作为例子的一个单独的数据项</SPAN></STRONG></EM></P>
<P><EM><STRONG><SPAN style="FONT-FAMILY: 宋体; Times: ">LDAP复制</SPAN></STRONG></EM></P>
<P><STRONG><EM><SPAN style="FONT-FAMILY: 宋体; Times: ">安全和访问控制<BR></SPAN></EM></STRONG></P><SPAN style="FONT-FAMILY: 宋体; Times: ">
<H2><A name=_单独的LDAP记录></A><SPAN style="FONT-FAMILY: 黑体">单独的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 黑体">记录</SPAN></H2>
<H3><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">是</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">记录项的名字</SPAN></H3>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录中的所有记录项都有一个唯一的“</SPAN><SPAN lang=EN-US>Distinguished Name</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”，也就是</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。每一个</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">记录项的</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">是由两个部分组成的：相对</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（</SPAN><SPAN lang=EN-US>RDN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）和记录在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录中的位置。</SPAN></P>
<P><SPAN lang=EN-US>RDN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">是</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中与目录树的结构无关的部分。在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录中存储的记录项都要有一个名字，这个名字通常存在</SPAN><SPAN lang=EN-US>cn</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（</SPAN><SPAN lang=EN-US>Common Name</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）这个属性里。因为几乎所有的东西都有一个名字，在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中存储的对象都用它们的</SPAN><SPAN lang=EN-US>cn</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">值作为</SPAN><SPAN lang=EN-US>RDN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的基础。如果我把最喜欢的吃燕麦粥食谱存为一个记录，我就会用</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn=Oatmeal Deluxe</SPAN></B><SPAN style="FONT-FAMILY: 宋体; Times: ">作为记录项的</SPAN><SPAN lang=EN-US>RDN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">我的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录的基准</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">是</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">dc=foobar,dc=com</SPAN></B></P>
<P><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">我把自己的食谱作为</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的记录项存在</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">ou=recipes</SPAN></B></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">我的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">记录项的</SPAN><SPAN lang=EN-US>RDN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">设为</SPAN><B><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn=Oatmeal Deluxe</SPAN></B></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">上面这些构成了燕麦粥食谱的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">记录的完整</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。记住，</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的读法和</SPAN><SPAN lang=EN-US>DNS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">主机名类似。下面就是完整的</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">：</SPAN></P>
<P class=a><B><SPAN lang=EN-US>cn=Oatmeal Deluxe,ou=recipes,dc=foobar,dc=com</SPAN></B></P>
<H3><SPAN style="FONT-FAMILY: 宋体; Times: ">举一个实际的例子来说明</SPAN><SPAN lang=EN-US>DN</SPAN></H3>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">现在为公司的员工设置一个</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。可以用基于</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">或</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（</SPAN><SPAN lang=EN-US>User ID</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">），作为典型的用户帐号。例如，</SPAN><SPAN lang=EN-US>FooBar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的员工</SPAN><SPAN lang=EN-US>Fran Smith</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（登录名：</SPAN><SPAN lang=EN-US>fsmith</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）的</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">可以为下面两种格式：</SPAN></P>
<P class=a><B><SPAN lang=EN-US>uid=fsmith,ou=employees,dc=foobar,dc=com</SPAN></B></P>
<P><I><SPAN style="FONT-FAMILY: 宋体; Times: ">（基于登录名）</SPAN><SPAN lang=EN-US></SPAN></I></P>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（以及</SPAN><SPAN lang=EN-US>X.500</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）用</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">表示“</SPAN><SPAN lang=EN-US>User ID</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”，不要把它和</SPAN><SPAN lang=EN-US>UNIX</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的</SPAN><SPAN lang=EN-US>uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">号混淆了。大多数公司都会给每一个员工唯一的登录名，因此用这个办法可以很好地保存员工的信息。你不用担心以后还会有一个叫</SPAN><SPAN lang=EN-US>Fran Smith</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的加入公司，如果</SPAN><SPAN lang=EN-US>Fran</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">改变了她的名字（结婚？离婚？或宗教原因？），也用不着改变</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">记录项的</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN></P>
<P class=a><B><SPAN lang=EN-US>cn=Fran Smith,ou=employees,dc=foobar,dc=com</SPAN></B></P>
<P><I><SPAN style="FONT-FAMILY: 宋体; Times: ">（基于姓名）</SPAN><SPAN lang=EN-US></SPAN></I></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">可以看到这种格式使用了</SPAN><SPAN lang=EN-US>Common Name</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（</SPAN><SPAN lang=EN-US>CN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）。可以把</SPAN><SPAN lang=EN-US>Common Name</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">当成一个人的全名。这种格式有一个很明显的缺点就是：如果名字改变了，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的记录就要从一个</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">转移到另一个</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。但是，我们应该尽可能地避免改变一个记录项的</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN></P>
<H2><SPAN style="FONT-FAMILY: 黑体">定制目录的对象类型</SPAN></H2>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">你可以用</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">存储各种类型的数据对象，只要这些对象可以用属性来表示，下面这些是可以在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中存储的一些信息：</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">员工信息：员工的姓名、登录名、口令、员工号、他的经理的登录名，邮件服务器，等等。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">物品跟踪信息：计算机名、</SPAN><SPAN lang=EN-US>IP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">地址、标签、型号、所在位置，等等。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">客户联系列表：客户的公司名、主要联系人的电话、传真和电子邮件，等等。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">会议厅信息：会议厅的名字、位置、可以坐多少人、电话号码、是否有投影机。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">食谱信息：菜的名字、配料、烹调方法以及准备方法。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">因为</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录可以定制成存储任何文本或二进制数据，到底存什么要由你自己决定。</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录用对象类型（</SPAN><SPAN lang=EN-US>object classes</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）的概念来定义运行哪一类的对象使用什么属性。在几乎所有的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器中，你都要根据自己的需要扩展基本的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录的功能，创建新的对象类型或者扩展现存的对象类型。</SPAN></P>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录以一系列“属性对”的形式来存储记录项，每一个记录项包括属性类型和属性值（这与关系型数据库用行和列来存取数据有根本的不同）。下面是我存在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录中的一部分食谱记录：</SPAN></P>
<P class=a><SPAN>&nbsp; </SPAN>dn: cn=Oatmeal Deluxe, ou=recipes, dc=foobar, dc=com </P>
<P class=a><SPAN>&nbsp; </SPAN>cn: Instant Oatmeal Deluxe </P>
<P class=a><SPAN>&nbsp; </SPAN>recipeCuisine: breakfast </P>
<P class=a><SPAN>&nbsp; </SPAN>recipeIngredient: 1 packet instant oatmeal </P>
<P class=a><SPAN>&nbsp; </SPAN>recipeIngredient: 1 cup water </P>
<P class=a><SPAN>&nbsp; </SPAN>recipeIngredient: 1 pinch salt </P>
<P class=a><SPAN>&nbsp; </SPAN>recipeIngredient: 1 tsp brown sugar </P>
<P class=a><SPAN>&nbsp; </SPAN>recipeIngredient: 1/4 apple, any type</P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">请注意上面每一种配料都作为属性</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">recipeIngredient</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">值。</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录被设计成象上面那样为一个属性保存多个值的，而不是在每一个属性的后面用逗号把一系列值分开。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">因为用这样的方式存储数据，所以数据库就有很大的灵活性，不必为加入一些新的数据就重新创建表和索引。更重要的是，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录不必花费内存或硬盘空间处理“空”域，也就是说，实际上不使用可选择的域也不会花费你任何资源。</SPAN></P>
<H2><A name=_作为例子的一个单独的数据项></A><SPAN style="FONT-FAMILY: 黑体">作为例子的一个单独的数据项</SPAN></H2>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">让我们看看下面这个例子。我们用</SPAN><SPAN lang=EN-US>Foobar, Inc.</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的员工</SPAN><SPAN lang=EN-US>Fran Smith</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">记录。这个记录项的格式是</SPAN><SPAN lang=EN-US>LDIF</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，用来导入和导出</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录的记录项。</SPAN></P>
<P class=a><SPAN>&nbsp; </SPAN>dn: uid=fsmith, ou=employees, dc=foobar, dc=com</P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: person</P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: organizationalPerson</P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: inetOrgPerson</P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: foobarPerson</P>
<P class=a><SPAN>&nbsp; </SPAN>uid: fsmith</P>
<P class=a><SPAN>&nbsp; </SPAN>givenname: Fran</P>
<P class=a><SPAN>&nbsp; </SPAN>sn: Smith</P>
<P class=a><SPAN>&nbsp; </SPAN>cn: Fran Smith</P>
<P class=a><SPAN>&nbsp;</SPAN>&nbsp;cn: Frances Smith</P>
<P class=a><SPAN>&nbsp; </SPAN>telephonenumber: 510-555-1234</P>
<P class=a><SPAN>&nbsp; </SPAN>roomnumber: 122G</P>
<P class=a><SPAN>&nbsp; </SPAN>o: Foobar, Inc.</P>
<P class=a><SPAN>&nbsp; </SPAN>mailRoutingAddress: fsmith@foobar.com</P>
<P class=a><SPAN>&nbsp; </SPAN>mailhost: mail.foobar.com</P>
<P class=a><SPAN>&nbsp; </SPAN>userpassword: {crypt}3x1231v76T89N</P>
<P class=a><SPAN>&nbsp; </SPAN>uidnumber: 1234</P>
<P class=a><SPAN>&nbsp; </SPAN>gidnumber: 1200</P>
<P class=a><SPAN>&nbsp; </SPAN>homedirectory: /home/fsmith</P>
<P class=a><SPAN>&nbsp; </SPAN>loginshell: /usr/local/bin/bash</P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">属性的值在保存的时候是保留大小写的，但是在默认情况下搜索的时候是不区分大小写的。某些特殊的属性（例如，</SPAN><SPAN lang=EN-US>password</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）在搜索的时候需要区分大小写。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">让我们一点一点地分析上面的记录项。</SPAN></P>
<P class=a><SPAN lang=EN-US>dn: uid=fsmith, ou=employees, dc=foobar, dc=com</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">这是</SPAN><SPAN lang=EN-US>Fran</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">记录项的完整</SPAN><SPAN lang=EN-US>DN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，包括在目录树中的完整路径。</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（和</SPAN><SPAN lang=EN-US>X.500</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）使用</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（</SPAN><SPAN lang=EN-US>User ID</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">），不要把它和</SPAN><SPAN lang=EN-US>UNIX</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的</SPAN><SPAN lang=EN-US>uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">号混淆了。</SPAN></P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: person </P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: organizationalPerson </P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: inetOrgPerson </P>
<P class=a><SPAN>&nbsp; </SPAN>objectclass: foobarPerson</P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">可以为任何一个对象根据需要分配多个对象类型。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">person</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">对象类型要求</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（</SPAN><SPAN lang=EN-US>common name</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）和</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">sn</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">（</SPAN><SPAN lang=EN-US>surname</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）这两个域不能为空。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">persion</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">对象类型允许有其它的可选域，包括</SPAN><SPAN lang=EN-US>givenname</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">、</SPAN><SPAN lang=EN-US>telephonenumber</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，等等。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">organizational Person</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">给</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">person</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">加入更多的可选域，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">inetOrgPerson</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">又加入更多的可选域（包括电子邮件信息）。最后，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">foobarPerson</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">是为</SPAN><SPAN lang=EN-US>Foobar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">定制的对象类型，加入了很多定制的属性。</SPAN></P>
<P class=a><SPAN>&nbsp; </SPAN>uid: fsmith </P>
<P class=a><SPAN>&nbsp; </SPAN>givenname: Fran </P>
<P class=a><SPAN>&nbsp; </SPAN>sn: Smith </P>
<P class=a><SPAN>&nbsp; </SPAN>cn: Fran Smith </P>
<P class=a><SPAN>&nbsp; </SPAN>cn: Frances Smith </P>
<P class=a><SPAN>&nbsp; </SPAN>telephonenumber: 510-555-1234 </P>
<P class=a><SPAN>&nbsp; </SPAN>roomnumber: 122G </P>
<P class=a><SPAN>&nbsp; </SPAN>o: Foobar, Inc.</P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">以前说过了，</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">表示</SPAN><SPAN lang=EN-US>User ID</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。当看到</SPAN><SPAN lang=EN-US>uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的时候，就在脑袋里想一想“</SPAN><SPAN lang=EN-US>login</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">请注意</SPAN><SPAN lang=EN-US>CN</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">有多个值。就象上面介绍的，</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">允许某些属性有多个值。为什么允许有多个值呢？假定你在用公司的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器查找</SPAN><SPAN lang=EN-US>Fran</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的电话号码。你可能只知道她的名字叫</SPAN><SPAN lang=EN-US>Fran</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，但是对人力资源处的人来说她的正式名字叫做</SPAN><SPAN lang=EN-US>Frances</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。因为保存了她的两个名字，所以用任何一个名字检索都可以找到</SPAN><SPAN lang=EN-US>Fran</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的电话号码、电子邮件和办公房间号，等等。</SPAN></P>
<P class=a><SPAN>&nbsp; </SPAN>mailRoutingAddress: fsmith@foobar.com </P>
<P class=a><SPAN>&nbsp; </SPAN>mailhost: mail.foobar.com</P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">就象现在大多数的公司都上网了，</SPAN><SPAN lang=EN-US>Foobar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">用</SPAN><SPAN lang=EN-US>Sendmail</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">发送邮件和处理外部邮件路由信息。</SPAN><SPAN lang=EN-US>Foobar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">把所有用户的邮件信息都存在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中。最新版本的</SPAN><SPAN lang=EN-US>Sendmail</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">支持这项功能。</SPAN></P>
<P class=a><SPAN>&nbsp; </SPAN>Userpassword: {crypt}3x1231v76T89N </P>
<P class=a><SPAN>&nbsp; </SPAN>uidnumber: 1234 </P>
<P class=a><SPAN>&nbsp; </SPAN>gidnumber: 1200 </P>
<P class=a><SPAN>&nbsp; </SPAN>gecos: Frances Smith </P>
<P class=a><SPAN>&nbsp; </SPAN>homedirectory: /home/fsmith </P>
<P class=a><SPAN>&nbsp; </SPAN>loginshell: /usr/local/bin/bash</P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">注意，</SPAN><SPAN lang=EN-US>Foobar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的系统管理员把所有用户的口令映射信息也都存在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中。</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">FoobarPerson</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">类型的对象具有这种能力。再注意一下，用户口令是用</SPAN><SPAN lang=EN-US>UNIX</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的口令加密格式存储的。</SPAN><SPAN lang=EN-US>UNIX</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的</SPAN><SPAN lang=EN-US>uid</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">在这里为</SPAN><SPAN lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uidnumber</SPAN><SPAN style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 黑体">。</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">提醒你一下，关于如何在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">中保存</SPAN><SPAN lang=EN-US>NIS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">信息，有完整的一份</SPAN><SPAN lang=EN-US>RFC</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。在以后的文章中我会谈一谈</SPAN><SPAN lang=EN-US>NIS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的集成。</SPAN></P>
<H2><A name=_LDAP复制></A><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 黑体">复制</SPAN></H2>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器可以使用基于“推”或者“拉”的技术，用简单或基于安全证书的安全验证，复制一部分或者所有的数据。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">例如，</SPAN><SPAN lang=EN-US>Foobar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">有一个“公用的”</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器，地址为</SPAN><SPAN lang=EN-US>ldap.foobar.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，端口为</SPAN><SPAN lang=EN-US>389</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN><SPAN lang=EN-US>Netscape Communicator</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的电子邮件查询功能、</SPAN><SPAN lang=EN-US>UNIX</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的“</SPAN><SPAN lang=EN-US>ph</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”命令要用到这个服务器，用户也可以在任何地方查询这个服务器上的员工和客户联系信息。公司的主</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器运行在相同的计算机上，不过端口号是</SPAN><SPAN lang=EN-US>1389</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">你可能即不想让员工查询资产管理或食谱的信息，又不想让信息技术人员看到整个公司的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录。为了解决这个问题，</SPAN><SPAN lang=EN-US>Foobar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">有选择地把子目录树从主</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器复制到“公用”</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器上，不复制需要隐藏的信息。为了保持数据始终是最新的，主目录服务器被设置成即时“推”同步。这些种方法主要是为了方便，而不是安全，因为如果有权限的用户想查询所有的数据，可以用另一个</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">端口。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">假定</SPAN><SPAN lang=EN-US>Foobar</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">通过从奥克兰到欧洲的低带宽数据的连接用</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">管理客户联系信息。可以建立从</SPAN><SPAN lang=EN-US>ldap.foobar.com:1389</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">到</SPAN><SPAN lang=EN-US>munich-ldap.foobar.com:389</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的数据复制，象下面这样：</SPAN></P>
<P class=a><SPAN>&nbsp; </SPAN>periodic pull: ou=asia,ou=customers,o=sendmail.com</P>
<P class=a><SPAN>&nbsp; </SPAN>periodic pull: ou=us,ou=customers,o=sendmail.com</P>
<P class=a><SPAN>&nbsp; </SPAN>immediate push: ou=europe,ou=customers,o=sendmail.com</P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">“拉”连接每</SPAN><SPAN lang=EN-US>15</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">分钟同步一次，在上面假定的情况下足够了。“推”连接保证任何欧洲的联系信息发生了变化就立即被“推”到</SPAN><SPAN lang=EN-US>Munich</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">用上面的复制模式，用户为了访问数据需要连接到哪一台服务器呢？在</SPAN><SPAN lang=EN-US>Munich</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的用户可以简单地连接到本地服务器。如果他们改变了数据，本地的</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器就会把这些变化传到主</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器。然后，主</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器把这些变化“推”回本地的“公用”</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器保持数据的同步。这对本地的用户有很大的好处，因为所有的查询（大多数是读）都在本地的服务器上进行，速度非常快。当需要改变信息的时候，最终用户不需要重新配置客户端的软件，因为</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录服务器为他们完成了所有的数据交换工作。</SPAN></P>
<H2><A name=_安全和访问控制></A><SPAN style="FONT-FAMILY: 黑体">安全和访问控制</SPAN></H2>
<P><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">提供很复杂的不同层次的访问控制或者</SPAN><SPAN lang=EN-US>ACI</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。因这些访问可以在服务器端控制，这比用客户端的软件保证数据的安全可安全多了。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">用</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">的</SPAN><SPAN lang=EN-US>ACI</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，可以完成：</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">给予用户改变他们自己的电话号码和家庭地址的权限，但是限制他们对其它数据（如，职务名称，经理的登录名，等等）只有“只读”权限。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">给予“</SPAN><SPAN lang=EN-US>HR-admins</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”组中的所有人权限以改变下面这些用户的信息：经理、工作名称、员工号、部门名称和部门号。但是对其它域没有写权限。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">禁止任何人查询</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器上的用户口令，但是可以允许用户改变他或她自己的口令。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">给予经理访问他们上级的家庭电话的只读权限，但是禁止其他人有这个权限。</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">给予“</SPAN><SPAN lang=EN-US>host-admins</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”组中的任何人创建、删除和编辑所有保存在</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">服务器中的与计算机主机有关的信息</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">通过</SPAN><SPAN lang=EN-US>Web</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，允许“</SPAN><SPAN lang=EN-US>foobar-sales</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">”组中的成员有选择地给予或禁止他们自己读取一部分客户联系数据的读权限。这将允许他们把客户联系信息下载到本地的笔记本电脑或个人数字助理（</SPAN><SPAN lang=EN-US>PDA</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）上。（如果销售人员的软件都支持</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，这将非常有用）</SPAN></P>
<P><SPAN lang=EN-US style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">通过</SPAN><SPAN lang=EN-US>Web</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">，允许组的所有者删除或添加他们拥有的组的成员。例如：可以允许销售经理给予或禁止销售人员改变</SPAN><SPAN lang=EN-US>Web</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">页的权限。也可以允许邮件假名（</SPAN><SPAN lang=EN-US>mail aliase</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">）的所有者不经过</SPAN><SPAN lang=EN-US>IT</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">技术人员就直接从邮件假名中删除或添加用户。“公用”的邮件列表应该允许用户从邮件假名中添加或删除自己（但是只能是自己）。也可以对</SPAN><SPAN lang=EN-US>IP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">地址或主机名加以限制。例如，某些域只允许用户</SPAN><SPAN lang=EN-US>IP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">地址以</SPAN><SPAN lang=EN-US>192.168.200.*</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">开头的有读的权限，或者用户反向查找</SPAN><SPAN lang=EN-US>DNS</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">得到的主机名必须为</SPAN><SPAN lang=EN-US>*.foobar.com</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体; Times: ">这不过是让你了解一下可以对</SPAN><SPAN lang=EN-US>LDAP</SPAN><SPAN style="FONT-FAMILY: 宋体; Times: ">目录进行怎样的访问控制，实际上真正实现起来需要做的工作比这多得多。在以后的文章中我会详细地讨论访问控制。</SPAN></P></SPAN><img src ="http://www.blogjava.net/qq13367612/aggbug/16876.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 15:46 <a href="http://www.blogjava.net/qq13367612/articles/16876.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CVS使用手册</title><link>http://www.blogjava.net/qq13367612/articles/16666.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16666.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16666.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16666.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16666.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16666.html</trackback:ping><description><![CDATA[<P>内容摘要： </P>
<P>CVS是一个C/S系统，多个开发人员通过一个中心版本控制系统来记录文件版本，从而达到保证文件同步的目的。工作模式如下： </P><PRE>       CVS服务器（文件版本库）
     /     |       \
     （版 本 同 步）
   /       |         \
开发者1  开发者2   开发者3
</PRE>
<P>作为一般开发人员挑选2,6看就可以了，CVS的管理员则更需要懂的更多一些，最后还简单介绍了一些Windows下的cvs客户端使用，CVS远程用户认证的选择及与BUG跟踪系统等开发环境的集成问题。</P>
<OL>
<LI><A href="http://www.chedong.com/tech/cvs_card.html#init">CVS环境初始化</A>：CVS环境的搭建 管理员 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#daily">CVS的日常使用</A>：日常开发中最常用的CVS命令， 开发人员 管理员 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#branch">CVS的分支开发</A>：项目按照不同进度和目标并发进行 管理员 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#ssh">CVS的用户认证</A>：通过SSH的远程用户认证，安全，简单 管理员 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#cvsweb">CVSWEB</A>：CVS的WEB访问界面大大提高代码版本比较的效率 管理员 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#tag">CVS TAG</A>：将$<SPAN style="FONT-WEIGHT: bold">Id</SPAN>$ 加入代码注释中，方便开发过程的跟踪开发人员 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#vss">CVS vs VSS</A>: CVS和Virsual SourceSafe的比较 开发人员 管理员 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#wincvs">WinCVS:</A> 通过SSH认证的WinCVS认证设置 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#cvstrac">基于CVSTrac的小组开发环境搭建</A>：通过CVSTrac实现web界面的CVS用户管理,集成的BUG跟踪和WIKI交流 
<LI><A href="http://www.chedong.com/tech/cvs_card.html#auth">CVS中的用户权限管理</A>：基于系统用户的CVS权限管理和基于CVSROOT/passwd的虚拟用户管理 <BR></LI></OL>
<P>一个系统20%的功能往往能够满足80%的需求，CVS也不例外，以下是CVS最常用的功能，可能还不到它全部命令选项的20%，作为一般开发人员平时会用cvs update和cvs commit就够了，更多的需求在实际应用过程中自然会出现，不时回头看看相关文档经常有意外的收获。</P>
<P><BR></P>
<H2><A name=init>CVS环境初始化</A></H2>环境设置：指定CVS库的路径CVSROOT 
<P>tcsh <BR>setenv CVSROOT /path/to/cvsroot <BR>bash <BR>CVSROOT=/path/to/cvsroot ; export CVSROOT </P>
<P>后面还提到远程CVS服务器的设置： <BR>CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH <BR><BR>初始化：CVS版本库的初始化。 <BR>cvs init </P>
<P>一个项目的首次导入 <BR>cvs import -m "write some comments here" project_name vendor_tag release_tag <BR>执行后：会将所有源文件及目录导入到/path/to/cvsroot/project_name目录下 <BR><I>vender_tag: 开发商标记 <BR>release_tag: 版本发布标记</I> </P>
<P>项目导出：将代码从CVS库里导出 <BR>cvs checkout project_name <BR><I>cvs 将创建project_name目录，并将最新版本的源代码导出到相应目录中。这个checkout和Virvual SourceSafe中的check out不是一个概念，相对于Virvual SourceSafe的check out是cvs update， check in是cvs commit。</I> <BR><BR></P>
<H2><A style="FONT-WEIGHT: normal" name=daily>CVS的日常使用</A><B> </B></H2>
<P></P>
<P><B>注意：第一次导出以后，就不是通过cvs checkout来同步文件了，而是要进入刚才cvs checkout project_name导出的project_name目录下进行具体文件的版本同步（添加，修改，删除）操作。</B> </P>
<P><U>将文件同步到最新的版本 <BR></U>cvs update <BR><I>不制定文件名，cvs将同步所有子目录下的文件，也可以制定某个文件名/目录进行同步 <BR></I>cvs update file_name <BR><I>最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次，并养成“先同步 后修改”的习惯，和Virvual SourceSafe不同，CVS里没有文件锁定的概念，所有的冲突是在commit之前解决，如果你修改过程中，有其他人修改并commit到了CVS 库中，CVS会通知你文件冲突，并自动将冲突部分用 <BR>&gt;&gt;&gt;&gt;&gt;&gt; <BR>content on cvs server <BR>&lt;&lt;&lt;&lt;&lt;&lt; <BR>content in your file <BR>&gt;&gt;&gt;&gt;&gt;&gt; <BR>标记出来，由你确认冲突内容的取舍。 <BR>版本冲突一般是在多个人修改一个文件造成的，但这种项目管理上的问题不应该指望由CVS来解决。</I> </P>
<P><U>确认修改写入到CVS库里</U> <BR>cvs commit -m "write some comments here" file_name </P>
<P><I>注意：CVS的很多动作都是通过cvs commit进行最后确认并修改的，最好每次只修改一个文件。在确认的前，还需要用户填写修改注释，以帮助其他开发人员了解修改的原因。如果不用写-m "comments"而直接确认`cvs commit file_name` 的话，cvs会自动调用系统缺省的文字编辑器(一般是vi)要求你写入注释。 <BR>注释的质量很重要：所以不仅必须要写，而且必须写一些比较有意义的内容：以方便其他开发人员能够很好的理解 <BR>不好的注释，很难让其他的开发人员快速的理解：比如： -m "bug fixed" 甚至 -m "" <BR>好的注释，甚至可以用中文: -m "在用户注册过程中加入了Email地址校验"</I> <BR><BR>修改某个版本注释：每次只确认一个文件到CVS库里是一个很好的习惯，但难免有时候忘了指定文件名，把多个文件以同样注释commit到CVS库里了，以下命令可以允许你修改某个文件某个版本的注释： <BR>cvs admin -m 1.3:"write some comments here" file_name <BR><BR><U>添加文件</U> <BR>创建好新文件后，比如：touch new_file <BR>cvs add new_file <BR><I>注意：对于图片，Word文档等非纯文本的项目，需要使用cvs add -kb选项按2进制文件方式导入(k表示扩展选项，b表示binary)，否则有可能出现文件被破坏的情况 <BR>比如： <BR>cvs add -kb new_file.gif <BR>cvs add -kb readme.doc</I> </P>
<P><I>如果关键词替换属性在首次导入时设置错了怎么办？ <BR></I>cvs admin -kkv new_file.css <BR><I><BR></I><SPAN style="TEXT-DECORATION: underline">然后确认修改并注释</SPAN> <BR>cvs ci -m "write some comments here" </P>
<P><U>删除文件</U> <BR>将某个源文件物理删除后，比如：rm file_name <BR>cvs rm file_name <BR>然后确认修改并注释 <BR>cvs ci -m "write some comments here" <BR>以上面前2步合并的方法为： <BR>cvs rm -f file_name <BR>cvs ci -m "why delete file" <BR><I>注意：很多cvs命令都有缩写形式：commit=&gt;ci; update=&gt;up; checkout=&gt;co/get; remove=&gt;rm;</I> </P>
<P><U>添加目录</U> <BR>cvs add dir_name <BR><BR><U>查看修改历史</U> <BR>cvs log file_name <BR>cvs history file_name <BR><BR><U>查看当前文件不同版本的区别</U> <BR>cvs diff -r1.3 -r1.5 file_name <BR>查看当前文件（可能已经修改了）和库中相应文件的区别 <BR>cvs diff file_name <BR>cvs的web界面提供了更方便的定位文件修改和比较版本区别的方法，具体安装设置请看后面的cvsweb使用 </P>
<P><U>正确的通过CVS恢复旧版本的方法</U>： <BR>如果用cvs update -r1.2 file.name <BR>这个命令是给file.name加一个STICK TAG： "1.2" ，虽然你的本意只是想将它恢复到1.2版本 <BR>正确的恢复版本的方法是：cvs update -p -r1.2 file_name &gt;file_name <BR>如果不小心已经加成STICK TAG的话：用cvs update -A 解决 </P>
<P><U>移动文件/文件重命名</U> <BR>cvs里没有cvs move或cvs rename，因为这两个操作是可以由先cvs remove old_file_name，然后cvs add new_file_name实现的。 </P>
<P><U>删除/移动目录</U> <BR>最方便的方法是让管理员直接移动，删除CVSROOT里相应目录（因为CVS一个项目下的子目录都是独立的，移动到$CVSROOT目录下都可以作为新的独立项目：好比一颗树，其实砍下任意一枝都能独立存活），对目录进行了修改后，要求其开发人员重新导出项目cvs checkout project_name 或者用cvs update -dP同步。 </P>
<P><U>项目发布导出不带CVS目录的源文件</U> <BR>做开发的时候你可能注意到了，每个开发目录下，CVS都创建了一个CVS/目录。里面有文件用于记录当前目录和CVS库之间的对应信息。但项目发布的时候你一般不希望把文件目录还带着含有CVS信息的CVS目录吧，这个一次性的导出过程使用cvs export命令，不过export只能针对一个TAG或者日期导出，比如： <BR>cvs export -r release1 project_name <BR>cvs export -D 20021023 project_name <BR>cvs export -D now project_name </P>
<H2><A name=branch>CVS Branch：项目多分支同步开发</A></H2>确认版本里程碑：多个文件各自版本号不一样，项目到一定阶段，可以给所有文件统一指定一个阶段里程碑版本号，方便以后按照这个阶段里程碑版本号导出项目，同时也是项目的多个分支开发的基础。 <BR>
<P>cvs tag release_1_0</P>
<P><U>开始一个新的里程碑</U>： <BR>cvs commit -r 2 标记所有文件开始进入2.x的开发 </P>
<P><I>注意：CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。</I> </P>
<P><U>版本分支的建立</U> <BR>在开发项目的2.x版本的时候发现1.x有问题，但2.x又不敢用，则从先前标记的里程碑：release_1_0导出一个分支 release_1_0_patch <BR>cvs rtag -b -r release_1_0 release_1_0_patch proj_dir </P>
<P>一些人先在另外一个目录下导出release_1_0_patch这个分支：解决1.0中的紧急问题， <BR>cvs checkout -r release_1_0_patch <BR>而其他人员仍旧在项目的主干分支2.x上开发 </P>
<P>在release_1_0_patch上修正错误后，标记一个1.0的错误修正版本号 <BR>cvs tag release_1_0_patch_1 </P>
<P>如果2.0认为这些错误修改在2.0里也需要，也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中： <BR>cvs update -j release_1_0_patch_1 </P>
<H2><A name=ssh>CVS的远程认证通过SSH远程访问CVS</A></H2>使用cvs本身基于pserver的远程认证很麻烦,需要定义服务器和用户组，用户名，设置密码等， <BR>
<P>常见的登陆格式如下： <BR>cvs -d :pserver:cvs_user_name@cvs.server.address:/path/to/cvsroot login <BR>例子： <BR>cvs -d :pserver:cvs@samba.org:/cvsroot login </P>
<P>不是很安全，因此一般是作为匿名只读CVS访问的方式。从安全考虑，通过系统本地帐号认证并通过SSH传输是比较好的办法，通过在客户机的 /etc/profile里设置一下内容： <BR>CVSROOT=:ext:$USER@cvs.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH <BR>所有客户机所有本地用户都可以映射到CVS服务器相应同名帐号了。 </P>
<P>比如:</P>
<P>CVS服务器是192.168.0.3，上面CVSROOT路径是/home/cvsroot，另外一台开发客户机是192.168.0.4，如果 tom在2台机器上都有同名的帐号，那么从192.168.0.4上设置了： <BR>export CVSROOT=:ext:tom@192.168.0.3:/home/cvsroot <BR>export CVS_RSH=ssh <BR>tom就可以直接在192.168.0.4上对192.168.0.3的cvsroot进行访问了（如果有权限的话） <BR>cvs checkout project_name <BR>cd project_name <BR>cvs update <BR>... <BR>cvs commit <BR></P>
<P>如果CVS所在服务器的SSH端口不在缺省的22，或者和客户端与CVS服务器端SSH缺省端口不一致，有时候设置了： <BR>:ext:$USER@test.server.address#port:/path/to/cvsroot <BR><BR>仍然不行，比如有以下错误信息： <BR>ssh: test.server.address#port: Name or service not known <BR>cvs [checkout aborted]: end of file from server (consult above messages if any) <BR><BR>解决的方法是做一个脚本指定端口转向（不能使用alias，会出找不到文件错误）： <BR>创建一个/usr/bin/ssh_cvs文件，假设远程服务器的SSH端口是非缺省端口：34567 <BR>#!/bin/sh <BR>/usr/bin/ssh -p 34567 "$@" <BR>然后：chmod +x /usr/bin/ssh_cvs <BR>并CVS_RSH=ssh_cvs; export CVS_RSH </P>
<P>注意：port是指相应服务器SSH的端口，不是指cvs专用的pserver的端口 <BR><BR></P>
<H2><A name=cvsweb>CVSWEB：提高文件浏览效率</A></H2>CVSWEB就是CVS的WEB界面，可以大大提高程序员定位修改的效率: <BR>
<P>使用的样例可以看：<A href="http://www.freebsd.org/cgi/cvsweb.cgi">http://www.freebsd.org/cgi/cvsweb.cgi</A> </P>
<P>CVSWEB的下载：CVSWEB从最初的版本已经演化出很多功能界面更丰富的版本，这个是我个人感觉安装设置比较方便的： <BR>原先在：<S>http://www.spaghetti-code.de/software/linux/cvsweb/</S>，但目前已经删除，<A href="http://www.chedong.com/tech/cvsweb.tgz">目前仍可以在本站下载CVSWEB</A>，其实最近2年<A href="http://www.freebsd.org/projects/cvsweb.html">FreeBSD的CVSWeb项目</A>已经有了更好的发展吧，而当初没有用FreeBSD那个版本主要就是因为没有彩色的文件Diff功能。 <BR>下载解包： <BR>tar zxf cvsweb.tgz <BR>把配置文件cvsweb.conf放到安全的地方（比如和apache的配置放在同一个目录下）， <BR>修改：cvsweb.cgi让CGI找到配置文件： <BR>$config = $ENV{'CVSWEB_CONFIG'} || '/path/to/apache/conf/cvsweb.conf'; <BR><BR>转到/path/to/apache/conf下并修改cvsweb.conf： </P>
<OL>
<LI>修改CVSROOT路径设置： <BR>%CVSROOT = ( <BR>'Development' =&gt; '/path/to/cvsroot', #&lt;==修改指向本地的CVSROOT <BR>); 
<LI>缺省不显示已经删除的文档： <BR>"hideattic" =&gt; "1",#&lt;==缺省不显示已经删除的文档 
<LI>在配置文件cvsweb.conf中还可以定制页头的描述信息，你可以修改$long_intro成你需要的文字 </LI></OL>
<P>CVSWEB可不能随便开放给所有用户，因此需要使用WEB用户认证： <BR>先生成 passwd: <BR>/path/to/apache/bin/htpasswd -c cvsweb.passwd user <BR><BR>修改httpd.conf: 增加 <BR>&lt;Directory "/path/to/apache/cgi-bin/cvsweb/"&gt; <BR>AuthName "CVS Authorization" <BR>AuthType Basic <BR>AuthUserFile /path/to/cvsweb.passwd <BR>require valid-user <BR>&lt;/Directory&gt; <BR><BR></P>
<H2><A name=tag>CVS TAGS: $Id: cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $</A></H2>将$Id: cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $ 加在程序文件开头的注释里是一个很好的习惯，cvs能够自动解释更新其中的内容成：file_name version time user_name 的格式，比如：cvs_card.txt,v 1.1 2002/04/05 04:24:12 chedong Exp，可以这些信息了解文件的最后修改人和修改时间 <BR>
<P><BR></P><PRE>几个常用的缺省文件：
default.php
&lt;?php
/*
 * Copyright (c) 2002 Company Name.
 * $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $
 */

?&gt;
====================================
Default.java: 注意文件头一般注释用 /* 开始 JAVADOC注释用 /** 开始的区别
/*
 * Copyright (c) 2002 MyCompany Name.
 * $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $
 */

package com.mycompany;

import java.;

/**
 * comments here
 */
public class Default {
    /**
     * Comments here
     * @param
     * @return
     */
    public toString() {

    }
}
====================================
default.pl:
#!/usr/bin/perl -w
# Copyright (c) 2002 Company Name.
# $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $

# file comments here

use strict;

</PRE>
<H2><A name=vss>CVS vs VSS</A></H2>
<P>CVS没有文件锁定模式，VSS在check out同时，同时记录了文件被导出者锁定。 </P>
<P>CVS的update和commit， VSS是get_lastest_version和check in </P>
<P>对应VSS的check out/undo check out的CVS里是edit和unedit </P>
<P>在CVS中，标记自动更新功能缺省是打开的，这样也带来一个潜在的问题，就是不用-kb方式添加binary文件的话在cvs自动更新时可能会导致文件失效。</P>
<P>$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $ $Date: 2003/11/09 07:57:11 $这样的标记在Virsual SourceSafe中称之为Keyword Explaination，缺省是关闭的，需要通过OPITION打开，并指定需要进行源文件关键词扫描的文件类型：*.txt,*.java, *.html...</P>
<P>对于Virsual SourceSafe和CVS都通用的TAG有： <BR>$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $ <BR>$Author: chedong $ <BR>$Date: 2003/11/09 07:57:11 $ <BR>$Revision: 1.9 $ </P>
<P>我建议尽量使用通用的关键词保证代码在CVS和VSS都能方便的跟踪。 </P>
<H2><A name=wincvs>WinCVS</A></H2>下载： <BR>
<P>cvs Windows客户端：目前稳定版本为1.2 <BR><A href="http://cvsgui.sourceforge.net/">http://cvsgui.sourceforge.net</A> <BR>ssh Windows客户端 <BR><A href="http://www.networksimplicity.com/openssh/">http://www.networksimplicity.com/openssh/</A> <BR><BR>安装好以上2个软件以后： <BR>WinCVS客户端的admin==&gt;preference设置 <BR>1 在general选单里 <BR>设置CVSROOT： username@192.168.0.123:/home/cvsroot <BR>设置Authorization: 选择SSH server <BR><BR>2 Port选单里 <BR>钩上：check for alternate rsh name <BR>并设置ssh.exe的路径，缺省是装在 C:\Program Files\NetworkSimplicity\ssh\ssh.exe </P>
<P>然后就可以使用WinCVS进行cvs操作了，所有操作都会跳出命令行窗口要求你输入服务器端的认证密码。 </P>
<P>当然，如果你觉得这样很烦的话，还有一个办法就是生成一个没有密码的公钥/私钥对，并设置CVS使用基于公钥/私钥的SSH认证（在general 选单里）。</P>
<P>可以选择的diff工具：examdiff <BR>下载： <BR><A href="http://www.prestosoft.com/examdiff/examdiff.htm">http://www.prestosoft.com/examdiff/examdiff.htm</A> <BR>还是在WinCVS菜单admin==&gt;preference的WinCVS选单里 <BR>选上：Externel diff program <BR>并设置diff工具的路径，比如：C:\Program Files\ed16i\ExamDiff.exe <BR>在对文件进行版本diff时，第一次需要将窗口右下角的use externel diff选上。 </P>
<H2><A name=cvstrac>基于CVSTrac的小组开发环境搭建</A></H2>作为一个小组级的开发环境，版本控制系统和BUG跟踪系统等都涉及到用户认证部分。如何方便的将这些系统集成起来是一个非常困难的事情，毕竟我们不能指望 Linux下有像Source Offsite那样集成度很高的版本控制/BUG跟踪集成系统。 <BR><BR>我个人是很反对使用pserver模式的远程用户认证的，但如果大部分组员使用WINDOWS客户端进行开发的话，总体来说使用 CVSROOT/passwd认证还是很难避免的，但CVS本身用户的管理比较麻烦。本来我打算自己用perl写一个管理界面的，直到我发现了 CVSTrac：一个基于WEB界面的BUG跟踪系统，它外挂在CVS系统上的BUG跟踪系统，其中就包括了WEB界面的CVSROOT/passwd文件的管理，甚至还集成了WIKIWIKI讨论组功能。 
<P>这里首先说一下CVS的pserver模式下的用户认证，CVS的用户认证服务是基于inetd中的： <BR>cvspserver stream tcp nowait apache /usr/bin/cvs cvs --allow-root=/home/cvsroot pserver <BR>一般在2401端口（这个端口号很好记：49的平方） <BR><BR>CVS用户数据库是基于CVSROOT/passwd文件，文件格式： <BR>[username]:[crypt_password]:[mapping_system_user] <BR>由于密码都用的是UNIX标准的CRYPT加密，这个passwd文件的格式基本上是apache的htpasswd格式的扩展（比APACHE的 PASSWD文件多一个系统用户映射字段），所以这个文件最简单的方法可以用 <BR>apache/bin/htpasswd -b myname mypassword <BR>创建。注意：通过htpasswd创建出来的文件会没有映射系统用户的字段 <BR>例如： <BR>new:geBvosup/zKl2 <BR>setup:aISQuNAAoY3qw <BR>test:hwEpz/BX.rEDU </P>
<P>映射系统用户的目的在于：你可以创建一个专门的CVS服务帐号，比如用apache的运行用户apache，并将/home/cvsroot目录下的所有权限赋予这个用户，然后在passwd文件里创建不同的开发用户帐号，但开发用户帐号最后的文件读写权限都映射为apache用户，在SSH模式下多个系统开发用户需要在同一个组中才可以相互读写CVS库中的文件。</P>
<P>进一步的，你可以将用户分别映射到apache这个系统用户上。 <BR>new:geBvosup/zKl2:apache <BR>setup:aISQuNAAoY3qw:apache <BR>test:hwEpz/BX.rEDU:apache </P>
<P>CVSTrac很好的解决了CVSROOT/passwd的管理问题，而且包含了BUG跟踪报告系统和集成WIKIWIKI交流功能等，使用的 CGI方式的安装，并且基于<A href="http://www.gnu.org/copyleft/gpl.html">GNU Public License</A>： <BR></P>
<P align=left>在inetd里加入cvspserver服务： <BR>cvspserver stream tcp nowait apache /usr/bin/cvs cvs --allow-root=/home/cvsroot pserver </P>
<P align=left>xietd的配置文件：%cat cvspserver <BR>service cvspserver <BR>{ <BR>disable = no <BR>socket_type = stream <BR>wait = no <BR>user = apache <BR>server = /usr/bin/cvs <BR>server_args = -f --allow-root=/home/cvsroot pserver <BR>log_on_failure += USERID <BR>} </P>
<P>注意：这里的用户设置成apache目的是和/home/cvsroot的所有用户一致，并且必须让这个这个用户对/home/cvsroot/下的 CVSROOT/passwd和cvstrac初始化生成的myproj.db有读取权限。</P>
<P></P>
<P>安装过程 </P>
<OL>
<LI>下载：可以从<A href="http://www.cvstrac.org/">http://www.cvstrac.org</A> 下载 <BR>我用的是已经在Linux上编译好的应用程序包：cvstrac-1.1.2.bin.gz， <BR>%gzip -d cvstrac-1.1.2.bin.gz <BR>%chmod +x cvstrac-1.1.2.bin <BR>#mv cvstarc-1.1.1.bin /usr/bin/cvstrac <BR>如果是从源代码编译： <BR>从 http://www.sqlite.org/download.html 下载SQLITE的rpm包： <BR>rpm -i sqlite-devel-2.8.6-1.i386.rpm <BR>从 ftp://ftp.cvstrac.org/cvstrac/ 下载软件包 <BR>解包，假设解包到/home/chedong/cvstrac-1.1.2下，并规划将cvstrac安装到/usr/local/bin目录下， cd /home/chedong/cvstrac-1.1.2 编辑linux-gcc.mk: <BR>修改： <BR>SRCDIR = /home/chedong/cvstrac-1.1.2 <BR>INSTALLDIR = /usr/local/bin <BR>然后 <BR>mv linux-gcc.mk Makefile <BR>make <BR>#make install <BR><BR>
<LI>初始化cvstrac数据库：假设数据库名是 myproj <BR>在已经装好的CVS服务器上（CVS库这时候应该已经是初始化好了，比如：cvs init初始化在/home/cvsroot里），运行一下 <BR>%cvstrac init /home/cvsroot myproj <BR>运行后，/home/cvsroot里会有一个的myproj.db库，使用CVSTRAC服务，/home/cvsroot/myproj.db /home/cvsroot/CVSROOT/readers /home/cvsroot/CVSROOT/writers /home/cvsroot/CVSROOT/passwd这几个文件对于web服务的运行用户应该是可写的，在RedHat8上，缺省就有一个叫 apache用户和一个apache组，所以在httpd.conf文件中设置了用apache用户运行web服务： <BR>User apache <BR>Group apache， <BR>然后设置属于apache用户和apache组 <BR>#chown -R apache:apache /home/cvsroot <BR>-rw-r--r-- 1 apache apache 55296 Jan 5 19:40 myproj.db <BR>drwxrwxr-x 3 apache apache 4096 Oct 24 13:04 CVSROOT/ <BR>drwxrwxr-x 2 apache apache 4096 Aug 30 19:47 some_proj/ <BR>此外还在/home/cvsroot/CVSROOT中设置了： <BR>chmod 664 readers writers passwd <BR>
<LI>在apche/cgi-bin目录中创建脚本cvstrac: <BR>#!/bin/sh <BR>/usr/bin/cvstrac cgi /home/cvsroot <BR>设置脚本可执行： <BR>chmod +x /home/apache/cgi-bin/cvstrac <BR>
<LI>从 http://cvs.server.address/cgi-bin/cvstrac/myproj 进入管理界面 <BR>缺省登录名：setup 密码 setup <BR>对于一般用户可以从： <BR>http://cvs.server.address/cgi-bin/cvstrac/myproj 
<LI>在setup中重新设置了CVSROOT的路径后，/home/cvsroot <BR>如果是初次使用需要在/home/cvsroot/CVSROOT下创建passwd, readers, writers文件 <BR>touch passwd readers writers <BR>然后设置属于apache用户， <BR>chown apache.apache passwd readers writers <BR>这样使用setup用户创建新用户后会同步更新CVSROOT/passwd下的帐号 <BR></LI></OL>
<P align=left>修改登录密码，进行BUG报告等， <BR>更多使用细节可以在使用中慢慢了解。 <BR></P>
<P align=left>对于前面提到的WinCVS在perference里设置： <BR>CVSROOT栏输入：username@ip.address.of.cvs:/home/cvsroot <BR>Authenitication选择：use passwd file on server side <BR>就可以了从服务器上进行CVS操作了。 <BR></P>
<H2><A name=auth>CVS的用户权限管理</A></H2>
<P align=left>CVS的权限管理分2种策略： <BR></P>
<UL>
<LI>基于系统文件权限的系统用户管理：适合多个在Linux上使用系统帐号的开发人员进行开发。 
<LI>基于CVSROOT/passwd的虚拟用户管理：适合多个在Windows平台上的开发人员将帐号映射成系统帐号使用。 </LI></UL>为什么使用apache/apache用户？首先RedHat8中缺省就有了，而且使用这个用户可以方便通过cvstrac进行WEB管理。 <BR>chown -R apache.apache /home/cvsroot <BR>chmod 775 /home/cvsroot <BR>
<P align=left>Linux上通过ssh连接CVS服务器的多个开发人员：通过都属于apache组实现文件的共享读写 <BR>开发人员有开发服务器上的系统帐号：sysuser1 sysuser2，设置让他们都属于apache组，因为通过cvs新导入的项目都是对组开放的：664权限的，这样无论那个系统用户导入的项目文件，只要文件的组宿主是apache，所有其他同组系统开发用户就都可以读写；基于ssh远程认证的也是一样。 <BR></P>
<P align=left>&nbsp; &nbsp;apache(system group) <BR>/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \ <BR>sysuser1 &nbsp; sysuser2 &nbsp; &nbsp; sysuser3 <BR></P>
<P align=left>Windows上通过cvspserver连接CVS服务器的多个开发人员：通过在passwd文件种映射成 apache用户实现文件的共享读写 <BR>他们的帐号通过CVSROOT/passwd和readers writers这几个文件管理；通过cvstrac设置所有虚拟用户都映射到apache用户上即可。 <BR></P>
<P align=left>&nbsp; &nbsp;apache(system user) <BR>/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\ <BR>windev1 &nbsp; &nbsp; windev2 &nbsp; &nbsp; &nbsp;windev3&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </P>
<P align=left></P>
<P></P>
<P>利用cvs + (WinCVS/cvsweb/cvstrac)，构成了一个相对完善的跨平台工作组开发版本控制环境。</P>
<P>相关资源： </P>
<P>CVS HOME： <BR><A href="http://www.cvshome.org/">http://www.cvshome.org</A> </P>
<P>CVS FAQ： <BR><A href="http://www.loria.fr/~molli/cvs-index.html">http://www.loria.fr/~molli/cvs-index.html</A> <BR><BR>相关网站: <BR><A href="http://directory.google.com/Top/Computers/Software/Configuration_Management/Tools/Concurrent_Versions_System/">http://directory.google.com/Top/Computers/Software/Configuration_Management/Tools/Concurrent_Versions_System/</A> <BR></P>
<P>CVS--并行版本系统 <BR><A href="http://www.soforge.com/cvsdoc/zh_CN/book1.html">http://www.soforge.com/cvsdoc/zh_CN/book1.html</A> </P>
<P>CVS 免费书: <BR><A href="http://cvsbook.red-bean.com/">http://cvsbook.red-bean.com/</A> </P>
<P>CVS 命令的速查卡片： <BR><A href="http://www.refcards.com/about/cvs.html">http://www.refcards.com/about/cvs.html</A> </P>
<P>WinCVS: <BR><A href="http://cvsgui.sourceforge.net/">http://cvsgui.sourceforge.net/</A> </P>
<P>CVSTrac: A Web-Based Bug And Patch-Set Tracking System For CVS <BR><A href="http://www.cvstrac.org/">http://www.cvstrac.org</A> </P>
<P>StatCVS：基于CVS的代码统计工具：按代码量，按开发者的统计表等 <BR><A href="http://sourceforge.net/projects/statcvs">http://sourceforge.net/projects/statcvs <BR></A></P>如何在WEB开发中规划CVS上：在Google上查 "cvs web development" <BR><A href="http://ccm.redhat.com/bboard-archive/cvs_for_web_development/index.html">http://ccm.redhat.com/bboard-archive/cvs_for_web_development/index.html</A> <BR><BR>一些集成了CVS的IDE环境： <BR><A href="http://www.eclipse.org/">Eclipse</A> <BR><A href="http://www.magicunix.com/product_ch.html">Magic C++</A> <BR><img src ="http://www.blogjava.net/qq13367612/aggbug/16666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 22:23 <a href="http://www.blogjava.net/qq13367612/articles/16666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎么做需求分析</title><link>http://www.blogjava.net/qq13367612/articles/16020.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 14 Oct 2005 14:29:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16020.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16020.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16020.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16020.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16020.html</trackback:ping><description><![CDATA[<P>在具体的研究需求分析之前，我们先了解一下软件工程这个概念。软件工程分为三个层次，过程层、方法层、工具层。在最基础的过程层，最重要的就是一组被称为关键过程区域（KPAs）的框架（KPA的概念在讨论CMM的书中有详细的概念说明）。关键过程区域构成了软件项目的管理控制的基础，并且确立了上下文各区域的关系，其中规定了技术方法的采用、工程产品的，模型、文档、数据、报告、表格等，等的产生、里程碑的建立、质量的保证及变化的适当管理。方法层主要是过程在技术上的实现。它解决的问题是如何做。软件工程方法涵盖了一系列的任务：需求分析、设计、编程、测试、维护。同时他还包括了一组基本原则，控制了每一个的关键过程区域。工具层就很好理解了，他对过程层和方法层提供了自动和半自动的支持。这些辅助工具就称为CASE。<BR>　　可以看到需求分析的位置，但是事实上需求分析是跨越了软件工程的三个层次的。这一点是和其他的过程是一样的。当然我们这里比较重点强调的是在软件工程的方法层，同时也涉及到一些过程层的思想，至于工具层则不再我们的讨论之列，但是会提到一些很适合在需求分析时应用的工具，诸如Word、Excel、Visio等。<BR><FONT color=#ff0000>方法</FONT><BR>　　需求分析都包括了哪些方法呢？这里列举出在《需求分析》一书中推荐的一些方法，<BR>　　1) 绘制系统关联图，这种关联图是用于定义系统与系统外部实体间的界限和接口的简单模型。同时它也明确了通过接口的信息流和物质流。<BR>　　2) 创建用户接口原型，当开发人员或用户不能确定需求时，开发一个用户接口原型—一个可能的局部实现—这样使得许多概念和可能发生的事更为直观明了。用户通过评价原型将使项目参与者能更好地相互理解所要解决的问题。注意要找出需求文档与原型之间所有的冲突之处。<BR>　　3) 分析需求可行性，在允许的成本、性能要求下，分析每项需求实施的可行性，明确与每项需求实现相联系的风险，包括与其它需求的冲突，对外界因素的依赖和技术障碍。 　　4) 确定需求的优先级别，应用分析方法来确定使用实例、产品特性或单项需求实现的优先级别。以优先级为基础确定产品版本将包括哪些特性或哪类需求。当允许需求变更时，在特定的版本中加入每一项变更，并在那个版本计划中作出需要的变更。<BR>　　5) 为需求建立模型，需求的图形分析模型是软件需求规格说明极好的补充说明。它们能提供不同的信息与关系以有助于找到不正确的、不一致的、遗漏的和冗余的需求。这样的模型包括数据流图、实体关系图、状态变换图、对话框图、对象类及交互作用图。<BR>　　6) 创建数据字典，数据字典是对系统用到的所有数据项和结构的定义，以确保开发人员使用统一的数据定义。在需求阶段，数据字典至少应定义客户数据项以确保客户与开发小组是使用一致的定义和术语。分析和设计工具通常包括数据字典组件。<BR>　　7) 使用质量功能调配，（QFD）是一种高级系统技术，它将产品特性、属性与对客户的重要性联系起来。该技术提供了一种分析方法以明确那些是客户最为关注的特性。QFD将需求分为三类：期望需求，即客户或许并未提及，但如若缺少会让他们感到不满意；普通需求；兴奋需求，即实现了会给客户带去惊喜，但若未实现也不会受到责备（Zultner 1993;Pardee 1996）。<BR>　　记住一点，不要试图在你的项目中把这些方法都用上去，四个现代化并不是一夜就可以实现的。同样，尝试着使用你认为对你很有帮助的方法，确实收到效果之后，在考虑继续学习方法。因为上面提到的都是需求分析的大方法，事实上还有很多很多的方法可以采用，例如，采用SRS模板、指明需求的来源、为每项需求注上标号、记录业务规范、创建需求跟踪能力矩阵、审查需求文档、以需求为依据编写测试用例、编写用户手册、确定合格的标准。<BR><FONT color=#ff0000>业务建模</FONT><BR>　　很多人都没有意识到业务需求阶段应该做些什么事情，实际上业务建模是最重要的一件事情。不要觉得业务建模这个词很深奥，让人模不着头脑。其实所有做过需求分析的人都做过业务建模，比如你了解企业的运作模式就?是一种你脑海中的业务建模。但是大多数人都没有科学的、系统的、文档化的做过业务建模。<BR>　　业务建模的目的在于： <BR>　　了解目标组织（将要在其中部署系统的组织）的结构及机制。<BR>　　了解目标组织中当前存在的问题并确定改进的可能性。<BR>　　确保客户、最终用户和开发人员就目标组织达成共识。<BR>　　导出支持目标组织所需的业务需求。<BR>　　上面的话是不是很抽象呢，其实没有什么复杂的：人和电脑是完全不同的思想（思维方式）。所以，原先适合人的业务流程对于计算机来说可不一定合适的，为了最大限度的利用计算机，必须要了解原先的业务流程并对此加易改造（流程自动化），当然这些动作需要得到用户的许可。有些人认为说只有ERP这种大系统才需要对业务流程进行重组，但是实际上，不论是部门级的MIS系统，还是社会级的电子商务系统，都需要对业务流程进行改造，所不同的只是改造的程度。<BR>　　业务建模很重要的一点是在分析企业流程的同时分析出基础企业对象（Common Business Object）（这个词我翻译的不好，如果大家有更好的翻译，请告诉我）。任何企业都有最基础的一些元素，例如银行的CBO就有帐户，制造业的CBO就有订单等。有一次我的一个在企业应用方面研究多年的朋友告诉我一个秘诀，他说，企业的CBO无非是4个：客户、员工、产品和供应商（银行的供应商应该称为同业）。其他的所有CBO都是在这四个CBO的基础上发展起来的。比如说CBO中客户和产品是多对多的关系，根据关系数据的理论，任何多对多的关系都可以拆分成多个一对多或一对一的关系。你就可以在这两个类之间引入订单类，客户和订单之间是一对多，订单和产品之间又是一对多，这样一个多对多的关系就拆分成两个一对多的关系，而新的订单类也就顺理成章的产生了。在订单类产生时，你可能还会加入一个关联类：业务员类。而业务员类又是从员工类继承下来的。所以呢，企业的四种CBO通过不同的组合，不同的关系，能够形成企业运作的许许多多的CBO。<BR>　　CBO是做业务建模的基础，在此基础上，通过评估业务状态，说明当前业务，确定业务流程，改进业务流程的定义，设计业务流程实现，改进角色和职责，研究流程自动化，开发领域模型等一系列在RUP中定义的工作流程实现业务建模的目标。<BR><FONT color=#ff0000>需求获取</FONT><BR>　　需求获取(requirement elicitation)是需求工程的主体。对于所建议的软件产品，获取需求是一个确定和理解不同用户类的需要和限制的过程。获取用户需求位于软件需求三个层次的中间一层。业务需求决定用户需求，它描述了用户利用系统需要完成的任务。从这些任务中，分析者能获得用于描述系统活动的特定的软件功能需求，这些系统活动有助于用户执行他们的任务。 需求获取是在问题及其最终解决方案之间架设桥梁的第一步。获取需求的一个必不可少的结果是对项目中描述的客户需求的普遍理解。一旦理解了需求，分析者、开发者和客户就能探索出描述这些需求的多种解决方案。参与需求获取者只有在他们理解了问题之后才能开始设计系统，否则，对需求定义的任何改进，设计上都必须大量的返工。把需求获取集中在用户任务上—而不是集中在用户接口上—有助于防止开发组由于草率处理设计问题而造成的失误。 需求获取、分析、编写需求规格说明和验证并不遵循线性的顺序，这些活动是相互隔开、增量和反复的。当你和客户合作时，你就将会问一些问题，并且取得他们所提供的信息（需求获取）。同时，你将处理这些信息以理解它们，并把它们分成不同的类别，还要把客户需求同可能的软件需求相联系（分析）。然后，你可以使客户信息结构化，并编写成文档和示意图（说明）。下一步，就可以让客户代表评审文档并纠正存在的错误（验证）。这四个过程贯穿着需求分析的整个阶段。 需求获取可能是软件开发中最困难、最关键、最易出错及最需要交流的方面。需求获取只有通过有效的客户—开发者的合作才能成功。分析者必须建立一个对问题进行彻底探讨的环境，而这些问题与产品有关。为了方便清晰地进行交流，就要列出重要的小组，而不是假想所有的参与者都持有相同的看法。对需求问题的全面考察需要一种技术，利用这种技术不但考虑了问题的功能需求方面，还可讨论项目的非功能需求。确定用户已经理解：对于某些功能的讨论并不意味着即将在产品中实现它。对于想到的需求必须集中处理并设定优先级?，以避免一个不能带来任何益处的无限大的项目。 需求获取是一个需要高度合作的活动，而并不是客户所说的需求的简单誊本。作为一个分析者，你必须透过客户所提出的表面需求理解他们的真正需求。询问一个可扩充（open-ended）的问题有助于你更好地理解用户目前的业务过程并且知道新系统如何帮助或改进他们的工作。调查用户任务可能遇到的变更，或者用户需要使用系统其它可能的方式。想像你自己在学习用户的工作，你需要完成什么任务？你有什么问题？从这一角度来指导需求的开发和利用。<BR>　　还有，探讨例外的情况：什么会妨碍用户顺利完成任务？对系统错误情况的反映，用户是如何想的？询问问题时，以“还有什么能” ,”当?时，将会发生什么”“你有没有曾经想过” ,“有没有人曾经”为开头。记下每一个需求的来源，这样向下跟踪直到发现特定的客户。<BR>　　有些时候，尝试着问一些“愚蠢”的问题也有助于客户打开话匣子。如果你直接要求客户写出业务是如何实现的，客户十有八九无法完成。但是如果你尝试着问一些实际的问题，例如：“以我的理解，你们收到订单后，会...”。客户立刻就会指出你的错误，并滔滔不绝的开始谈论业务，而你，就在一边仔细的聆听吧。这一招就叫做“抛砖引玉”。<BR>　　需求讨论会上必须要使用笔记本电脑，还要指定一个打字熟练的人把所有的讨论记录下来，记录的同时还要做一定的整理。如果不这样做，那么你结束会议的时候就会发现，所有的讨论只剩下一个模糊的印象，需求对你来说仍然是一件遥远的事情。在座谈讨论之后，记下所讨论的条目(item)，并请参与讨论的用户评论并更正。及早并经常进行座谈讨论是需求获取成功的一个关键途径，因为只有提供需求的人才能确定是否真正获取需求。进行深入收集和分析以消除任何冲突或不一致性。<BR>　　尽量把客户所持的假设解释清楚，特别是那些发生冲突的部分。从字里行间去理解以明确客户没有表达清楚但又想加入的特性或特征。Gause 和Weinberg（1989）提出使用“上下文无关问题”—这是一个高层次的问题，它可以获取业务问题和可能的解决方案的全部信息。客户对这些问题的回答诸如“产品要求怎样的精确度”或“你能帮我解释一下你为什么不同意某人的回答吗？”这些回答可以更直接地认识问题，而这是封闭（close-end）问题所不能做到的。<BR>　　需求获取利用了所有可用的信息来源，这些信息描述了问题域或在软件解决方案中合理的特性。一个研究表明：比起不成功的项目，一个成功的项目在开发者和客户之间采用了更多的交流方式（Kiel and Carmel 1995）。与单个客户或潜在的用户组一起座谈，对于业务软件包或信息管理系统（MIS）的应用来说是一种传统的需求来源。直接聘请用户进行获取需求的过程是为项目获得支持和买入（buy-in）的一种方式。<BR>　　尽量理解用户用于表述他们需求的思维过程。充分研究用户执行任务时作出决策的过程，并提取出潜在的逻辑关系。流程图和决策树是描述这些逻辑决策途径的好方法。<BR>　　在需求获取的过程中，你可能会发现对项目范围的定义存在误差，不是太大就是太小。如果范围太大，你将要收集比真正需要更多的需求，以传递足够的业务和客户的值，此时获取过程将会拖延。如果项目范围太小，那么客户将会提出很重要的但又在当前产品范围之外的需求。当前的范围太小，以致不能提供一个令人满意的产品。需求的获取将导致修改项目的范围和任务，但作出这样具有深远影响的改变，一定要小心谨慎。 正如经常所说的，需求主要是关于系统做什么，而解决方案如何实现是属于设计的范围。这样说虽然很简洁，但似乎过于简单化。需求的获取应该把重点放在“做什么”上，但在分析和设计之间还是存在一定的距离。你可以使用假设“怎么做”来分类并改善你对用户需求的理解。在需求的获取过程中，分析模型、屏幕图形和原型可以使概念表达得更加清楚，然后提供一个寻找错误和遗漏的办法。把你在需求开发阶段所形成的模型和屏幕效果看成是方便高效交流的概念性建议，而不应该看成是对设计者选择的一种限制。 需求获取讨论会中如果参与者过多，就会减慢进度。人数大致控制在5到7人是最好的。这些人包括客户、系统设计者、开发者和可视化设计者等主要工程角色。相反地，从极少的代表那里收集信息或者只听到呼声最高、最有舆论影响的用户的声音，也会造成问题。这将导致忽视特定用户类的重要的需求，或者其需求不能代表绝大多数用户的需要。最?好的权衡在于选择一些授权为他们的用户类发言的产品代表者，他们也被同组用户类的其它代表所支持。 没有一个简单、清楚的信号暗示你什么时候已完成需求获取。当客户和开发者与他们的同事聊天、阅读工业和商业上的文献及在早上沐浴时思考时，他们都将对潜在产品产生新的构思。你不可能全面收集需求，但是下列的提示将会暗示你在需求获取的过程中的返回点。<BR>　　1. 如果用户不能想出更多的使用实例，也许你就完成了收集需求的工作。用户总是按其重要性的顺序来确定使用实例的。<BR>　　2. 如果用户提出新的使用实例，但你可以从其它使用实例的相关功能需求中获得这些新的使用实例，这时也许你就完成了收集需求的工作。这些新的使用实例可能是你已获取的其它使用实例的可选过程。 　　3. 如果用户开始重复原先讨论过的问题，此时，也许你就完成了收集需求的工作。<BR>　　4. 如果所提出的新需求比你已确定的需求的优先级都低时，也许你就完成了收集需求的工作。<BR>　　5. 如果用户提出对将来产品的要求，而不是现在我们讨论的特定产品，也许你就完成了收集需求的工作。<BR>　　以上知识大致上讨论需求分析应该如何做，实际上对于需求分析的方法有很多很多，已经形成了一定的理论，当然这种理论比较偏向与方法学，而方法学的应用主要还是要靠个人。所以，大家在实际应用的时候，不妨结合自己的实际，有选择性的采用一些方法，那你就是成功的。 </P>
<P>多年来，分析者总是利用情节或经历来描述用户和软件系统的交互方式，从而获取需求（McGraw and Harbison 1997）。Ivar Jacobson（1992）把这种看法系统地阐述成用例（用例）的方法进行需求获取和建模。虽然用例来源于面向对象的开发环境，但是它也能应用在具有许多开发方法的项目中，因为用户并不关心你是怎样开发你的软件。而最重要的，用例的观点和思维过程带给需求开发的改变比起是否画正式的用例图显得更为重要。注意用户要利用系统做什么远远强于询问用户希望系统为他们做什么这一传统方法。 用例的重要功能是用画用例图的功能来鉴别和划分系统功能。它把系统分成角色（actor）和用例（用例）。角色(actor)表示系统用户能扮演的角色（role）。这些用户可能是人，可能是其他的计算机一些硬件或者甚至是其它软件系统，唯一的标准是它们必须要在被划分进用例的系统部分以外。它们必须能刺激系统部分并接收返回。用例描述了当角色给系统特定的刺激时系统的活动。这些活动被文本描述。它描述了触发用例的刺激的本质，输入和输出到其他活动者，和转换输入到输出的活动。用例文本通常也描述每一个活动在特殊的活动线时可能的错误和系统应采取的补救措施。 这样说可能会非常复杂，其实一个用例描述了系统和一个角色（actor）的交互顺序。用例被定义成系统执行的一系列动作，动作执行的结果能被指定角色察觉到。用例可以:<BR>　　用例捕获某些用户可见的需求，实现一个具体的用户目标。<BR>　　用例由角色激活，并提供确切的值给角色。<BR>　　用例可大可小，但它必须是对一个具体的用户目标实现的完整描述。在UML中，用例表示为一个椭圆。<BR>　　角色是指用户在系统中所扮演的角色。其图形化的表示是一个小人。在某些组织中很可能有许多角色实例（例如有很多个销售员），但就该系统而言，他们均起着同一种作用，扮演着相同的角色，所以用一个角色表示。一个用户也可以扮演多种角色。例如，一个高级营销人员既可以是贸易经理，也可以是普通的营销人员；一个营销人员也可以是售货员。在处理角色时，应考虑其作用，而不是人或工作名称，这一点是很重要的。<BR>　　我们使用不带箭头的线段将角色与用例连接到一起，表示两者之间交换信息，称之为通信联系。角色触发用例，并与用例进行信息交换。单个角色可与多个用例联系；反过来，一个用例可与多个角色联系。对同一个用例而言，不同角色有着不同的作用：他们可以从用例中取值，也可以参与到用例中。需要注意的是角色在用例图中是用类似人的图形来表示，尽管执行的，但角色未必是人。例如，角色也可以是一个外界系统，该外界系统可能需要从当前系统中获取信息，与当前系统有进行交互。<BR>　　一个用例可能包括完成某项任务的许多逻辑相关任务和交互顺序。因此，一个用例是相关的用法说明的集合，并且一个说明（scenario）是用例的实例。这种关系就像是类和对象的关系。在用例中，一个说明被视为事件的普通过程(normal course)，也叫作主过程，基本过程，普通流，或“满意之路” (happy path)。在描述普通过程时列出执行者和系统之间相互交互或对话的顺序。当这种交互结束时，执行者也达到了预期的目的。<BR>　　在用例中的其它说明可以描述为可选过程(alternative coruse)。可选过程也可促进成功地完成任务，但它们代表了任务的细节或用于完成任务的途径的变化部分。在交互序列中，普通过程可以在一些决策点上分解成可选过程，然后再重新汇成一个普通过程。<BR><FONT color=#ff0000>角色类和角色实例</FONT><BR>　　软件产品最终是给一些用户来使用的，而用户之间的差异是非常大的。造成差异的原因包括了对计算机的认知程度的不同，使用习惯的不同，在软件目标组织中所处的地位不同，地理位置不同，业务熟练程度不同。<BR>　　不同的用户都有自己一系列的功能需求和非功能需求。对电脑熟练程度不同的人可能就会有不同的要求，熟练程度低的用户可能希望有一个友好的界面，熟练程度高的用户可能更希望有快捷键或宏的操作以提高工作效率。考虑到用户的差异性，将用户分类并研究用户类的行为特征是非常有必要的。所以在做具体的需求之前，先将用户分局行为和特点进行分类，对于研究、收集用户的需求是非常有帮助的。<BR>　　可以利用一个简单的表格列出一些原始的分类，然后不断的完善这个表格。确认你的分类之间没有交集。并充分描述用户分类的行为，目的，要求等。在企业分析中，比较常见的分类可能包括，供应商，客户，部门等。<BR>　　就像C++中的类和对象一样，我们把分析出的用户分类称为“角色类”，把实际的用户称为“角色实例”。在得到用户分类之后，最重要的就是要选出用户代表，用户代表不仅仅是在需求阶段中参与项目，还必须对项目的全过程负责。用户代表能够代表用户分类的需求。抓住用户代表的需求就大致把握住了用户类的需求。当然，需求分析还是需要在用户中做大规模的调查的，只是要把重点放在用户代表上。<BR>　　确保和用户直接进行沟通！大家有没有玩过传话的游戏，可能看过。一群人排成一列，一句话从排头挨个向后传，到最后，那句话已经是面目全非了。所以，一定要保证项目组能够直接和用户接触。 对于和用户直接沟通这一点，一般的针对特定企业的应用系统当然是不成问题，可是如果是开发行业软件，和用户直接沟通就成为一件几乎是不可能的事情。在这种情况下，一般有几种解决的办法：<BR>　　做大规模的市场调查，针对你的目标市场做市场调查，并根据统计学的理论建立你的数学模型。这部分的工作效果最好，其性质有些象一些游戏公司会发布一些Demo版的游戏。可是对于一般的企业来说，这项工作费时费力，高昂的成本往往使大家知难而退。我的意见是，方法是非常好的，但是可以采用折衷的办法，例如选取有代表性的企业，为特定企业制作一个较小的版本并收集反馈意见等。这涉及到很多市场营销的内容，并不是我的专业所长，这里就不多弄斧了。 聘请行业专家，一个行业专家往往可以在项目需求方面发挥极为重要的作用。一个行业专家往往都有大量的行业经验和行业的人际关系网络。在产品的设计方面，这个行业专家提供很多宝贵的意见。在目前很多的软件的开发过程中都采用了这种方式。行业专家有两种：一种是在这个行业中有很深的资历，但是对软件技术并不熟悉；第二种是开发过同类软件的软件专家，这种人在开发同类软件过程中已经积累了大量的项目经验，并且具有软件开发的知识。这种方式是获取需求的最好的方式。 分析对比同类软件，微软在开发Office、Visual Studio的时候，也是参照了Lotus和Borland的成熟产品。这种方式的特点在于成本很低，比较适合和其他的方式配合使用。但是，要注意自己有没有触犯专利法。<BR><FONT color=#ff0000>需求的冲突</FONT><BR>　　有的时候，虽然已经将用户分类并选出了用户代表。但是需求的来源众多，往往会发生需求之间自相矛盾的事情。需求从四面八方收集来后，人们难以解决冲突，澄清模糊之处以及协调不一致之处。某些人还要对不可避免要发生的范围问题单独作出决定。在项目的早期阶段，你必须决定谁是需求问题的决策者。如果不清楚谁有权并且有责任来作出决策，或者授权的个人不愿意或不能作出决策，那么决策者的角色将自然而然地落在开发者身上。这是一个非常糟糕的选择，因为开发者通常没有足够多的信息和观点来作出业务上的决策。<BR>　　在软件项目中，谁将对需求作出决策的问题并没有统一的正确答案。分析员有时听从呼声高的或来自最高层人物的最大的需求。即便使用用户代表这一手段，必须解决来自不同用户类的相冲突的需求。通常，应尽可能由处于公司底层的人作出决策，因为他们与问题密切相关，并能得到关于这些问题的广泛信息。<BR>　　如果不同的用户类有不一致的需求，那么必须决策出满足哪一类用户的需求更为重要。了解可能使用产品的客户种类的信息和他们的用法与产品的业务目标的关系如何，将有助于你决定哪一个用户类所占份额最大。<BR>　　当开发者想象中的产品与客户需求冲突时，通常应该由客户作出决策。然而，不要陷到“客户总是对的”的陷阱中去，对他们百依百顺。现实中，客户并不总是对的。客户总是持有自己的观点，开发者必须理解并尊重这一观点。<BR><FONT color=#ff0000>用例</FONT><BR>　　在具体的需求过程中，有大的用例（业务用例），也有小的用例。主要是由于用例的范围决定的。用例像是一个黑盒，它没有包括任何和实现有关或是内部的一些信息。它很容易就被用户（也包括开发者）所理解（简单的谓词短语）。如果用例不足以表达足够的信息来支持系统的开发，就有必要把用例黑盒打开，审视其内部的结构，找出黑盒内部的Actor和用例。就这样通过不断的打开黑盒，分析黑盒，再打开新的黑盒。直到整个系统可以被清晰的了解为止。 </P>
<P></P>
<P><BR>　　为什么要采用这种分析方法呢？计算机系统除了在与外界系统、人员有一系列的交互，在系统内部也往往存在着复杂的交互。因此，在系统建模时，除了描述系统与外界的交互，同时还要描述系统内部的交互。传统的MIS系统中，系统与外界的交互较多。典型的，如ATM取款机：存在着大量的用户与ATM，ATM与其它系统的交互。而电信领域的系统，与外界的交互较少。例如，系统的输入可能仅仅是从交换机上采集信息，然后由系统进行处理。系统的复杂逻辑包含在系统内部处理的流程上，而非与外部系统的交互。建模主要任务是表达系统内部的交互。<BR>　　用例图适于表达交互，之所以上面使用了电信系统，是因为用例最早来自于Ericsson的交换机系统。当时，还是Ericsson雇员的Jacobson初步建立了用例图的概念，并于1994年提出了OOSE方法，其最大特点是面向用例（Use-Case），并在用例的描述中引入了外部角色的概念。用例的概念是精确描述需求的重要武器，比较适合支持商业工程和需求分析。随着用例的发展，用例被大量的 用于对功能进行描述。每个用例代表了系统与外部ACTOR的交互。可以采取顺序图来表达用例的具体操作程序。ACTOR用于确定系统的边界。<BR>　　ACTOR、用例可以从不同的层次来描述信息。采用该原则的原因有：<BR>　　1. 需求并不是在项目一开始就很明确，往往是随着项目的推进，逐渐细化。<BR>　　2. 人的认知往往具有层次的特性。从粗到细、从一般到特殊。采用不同的层次来描述，适于认知的过程。 使用用例开发系统的一般过程<BR>　　在开发过程的初始阶段，可以根据具体的项目特点，制订开发各个视图之间的关联原则，指导规范。在开发的过程中，视图的组织原则应不断进行维护、更新。<BR>　　识别ACTOR来识别系统与外界交互的实体。ACTOR具有特定领域的特征，例如：交换机（采集系统）、97信息系统等。在系统层次的ACTOR确定了系统的边界。<BR>　　识别用例。同ACTOR一样，用例具有不同层次。对较为概括的USECASE，需要细化。注：系统开发需要一定的规则来确定，如何来分解用例；可能基于原有系统的经验，或是参考现有资料。<BR>　　当用例细化到可以被理解的层次。需要基于用例进行下一步的开发。如前面提到的，用例主要用来描述交互。因此，存在交互的实体和交互的细节。交互的实体采用类图来描述；而交互的细节，采用顺序图来描述。<BR>　　当系统复杂到一定层次时，类图和顺序图可能不能足以描述其复杂程度。在该情况下，需要使用状态图来辅助阐述。状态图和顺序图之间使用事件联结在一起。它们之间的一致性问题，目前UML和ROSE没有提供解决方案。相对折衷的方法是使用事件的命名规范强制一致性。<BR>　　可以说，之前说的所有的东西都是为了能够导出用例在需求中的作用。用例是从用户的角度看待系统，而不是基于程序员的角度。这样的话，用例驱动的系统能够真正做到以用户为中心，用户的任何需求都能够在系统开发链中完整的体现。用户和程序员间通过用例沟通，避免了牛头马嘴的尴尬局面。 从前，系统开发者总是通过情节来获取需求，是问用户希望系统为他做什么。在Jacobson发明了用例的概念之后，需求获取就变成问用户要利用系统做什么。这是立场不同导致的结果。用户通常并不关心系统是如何实现的（也有少数可爱的技术狂是例外）。对他们来说，更重要的是要达到他的目的。相反的，大部分的程序员的工作习惯就是考虑计算机应该如何实现用户的要求。所幸的是，用例方法能够调和双方的矛盾，因为虽然用例是来源于用户，服务于用户，但是它同样可以用于开发的流程。当系统的开发过程都是基于用例的，用用例获取需求，用用例设计，用用例编码，用用例测试的时候。这个开发过程就是用例驱动的。 用例和用例文档<BR>　　《软件需求》一书中提到了几种方法来确定用例：<BR>　　首先明确执行者和他们的角色，然后确定业务过程，在这一过程中每一个参与者都在为确定用例而努力。<BR>　　确定系统所能反映的外部事件，然后把这些事件与参与的执行者和特定的用例联系起来。<BR>　　以特定的说明形式表达业务过程或日常行为，从这些说明中获得用例，并确定参与到用例中的执行者，有可能从现在的功能需求说明中获得用例。如果有些需求与用例不一致，就应考虑是否真的需要它们。<BR>　　用例代表了用户的需求，在你的系统中，应该直接从不同用户类的代表或至少应从代理那里收集需求。用例为表达用户需求提供了一种方法，而这一方法必须与系统的业务需求相一致。分析者和用户必须检查每一个用例，在把它们纳入需求之前决定其是否在项目所定义的范围内。基于“用例”方法进行需求获取的目的在于：描述用户需要使用系统完成的所有任务。在理论上，用例的结果集将包括所有合理的系统功能。在现实中，你不可能获得完全包容，但是比起我所知道的其它获取方法，基于用例的方法可以为你带来更好的效果。 当使用用例进行需求获取时，应避免受不成熟的细节的影响。在对切合的客户任务取得共识之前，用户能很容易地在一个报表或对话框中列出每一项的精确设计。如果这些细节都作为需求记录下来，他们会给随后的设计过程带来不必要的限制。你可能要周期性地检查需求获取，以确保用户参与者将注意力集中在与今天所讨论的话题适合的抽象层上。向他们保证在开发过程中，将会详尽阐述他们的需求。在一个逐次详细描述过程中，重复地详述需求，以确定用户目标和任务，并作为用例。然后，把任务描述成功能需求，这些功能需求可以使用户完成其任务，也可以把它们描述成非功能需求，这些非功能需求描述了系统的限制和用户对质量的期望。虽然最初的屏幕构思有助于描述你对需求的理解，但是你必须细化用户界面设计。 建立用例文档。在每一次的需求获取之后，都会生成很多未整理的需求，你必须将它们组织成用例文档。使用诸如模板的技术能够提高你的速度和需求的复用性。一个用例文档可以使用表格来组织，主要的要素包括了用例标识号、用例名称、父用例标志号、创建者、创建时间、审核者、修订记录、角色、说明、先决条件、请求结果、优先级、普通过程、可选过程、例外、非功能需求、假设、注释和问题。 虽然列举出了这么多的属性，但是实际中使用的属性这要看你的团体而定，看项目的大小而定。把大量的时间花在用例的描述上是没有意义的。用户需要的是一个软件系统，并不是一大堆的用例说明。 </P><img src ="http://www.blogjava.net/qq13367612/aggbug/16020.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-14 22:29 <a href="http://www.blogjava.net/qq13367612/articles/16020.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式学习</title><link>http://www.blogjava.net/qq13367612/articles/16021.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 14 Oct 2005 14:22:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16021.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16021.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16021.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16021.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16021.html</trackback:ping><description><![CDATA[正则表达式(regular expression)描述了一种字符串匹配的模式，可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。<BR>　　列目录时，　dir *.txt或ls *.txt中的*.txt就<INS>不</INS>是一个正则表达式,因为这里*与正则式的*的含义是不同的。<A name=more></A>
<P>　　为便于理解和记忆，先从一些概念入手，所有特殊字符或字符组合有一个总表在后面，最后一些例子供理解相应的概念。</P>
<P>
<H3>正则表达式</H3><BR>　　是由普通字符（例如字符 a 到 z）以及特殊字符（称为元字符）组成的文字模式。正则表达式作为一个模板，将某个字符模式与所搜索的字符串进行匹配。<BR>　　可以通过在一对分隔符之间放入表达式模式的各种组件来构造一个正则表达式，即/expression/ 
<P></P>
<P>
<H3>普通字符</H3><BR>　　由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符，所有数字，所有标点符号以及一些符号。 <BR>
<H3>非打印字符</H3><BR>
<TABLE border=1>
<TBODY>
<TR>
<TD>字符 </TD>
<TD>含义</TD></TR>
<TR>
<TD>\cx </TD>
<TD>匹配由x指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 'c' 字符。</TD></TR>
<TR>
<TD>\f </TD>
<TD>匹配一个换页符。等价于 \x0c 和 \cL。</TD></TR>
<TR>
<TD>\n </TD>
<TD>匹配一个换行符。等价于 \x0a 和 \cJ。</TD></TR>
<TR>
<TD>\r </TD>
<TD>匹配一个回车符。等价于 \x0d 和 \cM。</TD></TR>
<TR>
<TD>\s </TD>
<TD>匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。</TD></TR>
<TR>
<TD>\S </TD>
<TD>匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。</TD></TR>
<TR>
<TD>\t </TD>
<TD>匹配一个制表符。等价于 \x09 和 \cI。</TD></TR>
<TR>
<TD>\v </TD>
<TD>匹配一个垂直制表符。等价于 \x0b 和 \cK。</TD></TR></TBODY></TABLE><BR>
<H3>特殊字符</H3><BR>　　所谓特殊字符，就是一些有特殊含义的字符，如上面说的"*.txt"中的*，简单的说就是表示任何字符串的意思。如果要查找文件名中有＊的文件，则需要对＊进行转义，即在其前加一个\。ls \*.txt。正则表达式有以下特殊字符。<BR>
<TABLE border=1>
<TBODY>
<TR>
<TD>特别字符</TD>
<TD>说明</TD></TR>
<TR>
<TD>$</TD>
<TD>匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性，则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身，请使用 \$。</TD></TR>
<TR>
<TD>( )</TD>
<TD>标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符，请使用 \( 和 \)。</TD></TR>
<TR>
<TD>*</TD>
<TD>匹配前面的子表达式零次或多次。要匹配 * 字符，请使用 \*。</TD></TR>
<TR>
<TD>+</TD>
<TD>匹配前面的子表达式一次或多次。要匹配 + 字符，请使用 \+。</TD></TR>
<TR>
<TD>.</TD>
<TD>匹配除换行符 \n之外的任何单字符。要匹配 .，请使用 \。</TD></TR>
<TR>
<TD>[ </TD>
<TD>标记一个中括号表达式的开始。要匹配 [，请使用 \[。</TD></TR>
<TR>
<TD>?</TD>
<TD>匹配前面的子表达式零次或一次，或指明一个非贪婪限定符。要匹配 ? 字符，请使用 \?。</TD></TR>
<TR>
<TD>\</TD>
<TD>将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如， 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\"，而 '\(' 则匹配 "("。</TD></TR>
<TR>
<TD>^</TD>
<TD>匹配输入字符串的开始位置，除非在方括号表达式中使用，此时它表示不接受该字符集合。要匹配 ^ 字符本身，请使用 \^。</TD></TR>
<TR>
<TD>{</TD>
<TD>标记限定符表达式的开始。要匹配 {，请使用 \{。</TD></TR>
<TR>
<TD>|</TD>
<TD>指明两项之间的一个选择。要匹配 |，请使用 \|。</TD></TR></TBODY></TABLE>
<P></P>
<P><BR><STRONG>　　构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。</STRONG></P>
<P><BR>
<H3>限定符</H3><BR>　　限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。<BR>*、+和?限定符都是贪婪的，因为它们会尽可能多的匹配文字，只有在它们的后面加上一个?就可以实现非贪婪或最小匹配。<BR>　　正则表达式的限定符有：<BR>
<TABLE border=1>
<TBODY>
<TR>
<TD>字符 </TD>
<TD>描述</TD></TR>
<TR>
<TD>* </TD>
<TD>匹配前面的子表达式零次或多次。例如，zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。</TD></TR>
<TR>
<TD>+ </TD>
<TD>匹配前面的子表达式一次或多次。例如，'zo+' 能匹配 "zo" 以及 "zoo"，但不能匹配 "z"。+ 等价于 {1,}。</TD></TR>
<TR>
<TD>? </TD>
<TD>匹配前面的子表达式零次或一次。例如，"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。</TD></TR>
<TR>
<TD>{n} </TD>
<TD>n 是一个非负整数。匹配确定的 n 次。例如，'o{2}' 不能匹配 "Bob" 中的 'o'，但是能匹配 "food" 中的两个 o。</TD></TR>
<TR>
<TD>{n,} </TD>
<TD>n 是一个非负整数。至少匹配n 次。例如，'o{2,}' 不能匹配 "Bob" 中的 'o'，但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。</TD></TR>
<TR>
<TD>{n,m} </TD>
<TD>m 和 n 均为非负整数，其中n <= m。最少匹配 n 次且最多匹配 m 次。例如，"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。</TD></TR></TBODY></TABLE>
<P></P>
<P>
<H3>定位符</H3><BR>　　用来描述字符串或单词的边界，^和$分别指字符串的开始与结束，\b描述单词的前或后边界，\B表示非单词边界。<INS>不能对定位符使用限定符。</INS> 
<P></P>
<P>
<H3>选择</H3><BR>　　用圆括号将所有选择项括起来，相邻的选择项之间用|分隔。但用圆括号会有一个副作用，是相关的匹配会被缓存，此时可用?:放在第一个选项前来消除这种副作用。<BR>　　其中?:是非捕获元之一，还有两个非捕获元是?=和?!，这两个还有更多的含义，前者为正向预查，在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串，后者为负向预查，在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。 
<P></P>
<P>
<H3>后向引用</H3><BR>　　对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中，所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从 1 开始，连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 '\n' 访问，其中 n 为一个标识特定缓冲区的一位或两位十进制数。<BR>　　可以使用非捕获元字符 '?:', '?=', or '?!' 来忽略对相关匹配的保存。 
<P></P>
<P>
<H3>各种操作符的运算优先级</H3><BR>　　相同优先级的从左到右进行运算，不同优先级的运算先高后低。各种操作符的优先级从高到低如下：<BR>
<TABLE border=1>
<TBODY>
<TR>
<TD>操作符 </TD>
<TD>描述</TD></TR>
<TR>
<TD>\ </TD>
<TD>转义符</TD></TR>
<TR>
<TD>(), (?:), (?=), [] </TD>
<TD>圆括号和方括号</TD></TR>
<TR>
<TD>*, +, ?, {n}, {n,}, {n,m} </TD>
<TD>限定符</TD></TR>
<TR>
<TD>^, $, \anymetacharacter </TD>
<TD>位置和顺序</TD></TR>
<TR>
<TD>| </TD>
<TD>“或”操作</TD></TR></TBODY></TABLE>
<P></P>
<P>
<H3>全部符号解释</H3><BR>
<TABLE border=1>
<TBODY>
<TR>
<TD>字符 </TD>
<TD>描述</TD></TR>
<TR>
<TD>\ </TD>
<TD>将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如，'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。</TD></TR>
<TR>
<TD>^ </TD>
<TD>匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性，^ 也匹配 '\n' 或 '\r' 之后的位置。</TD></TR>
<TR>
<TD>$ </TD>
<TD>匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性，$ 也匹配 '\n' 或 '\r' 之前的位置。</TD></TR>
<TR>
<TD>* </TD>
<TD>匹配前面的子表达式零次或多次。例如，zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。</TD></TR>
<TR>
<TD>+ </TD>
<TD>匹配前面的子表达式一次或多次。例如，'zo+' 能匹配 "zo" 以及 "zoo"，但不能匹配 "z"。+ 等价于 {1,}。</TD></TR>
<TR>
<TD>? </TD>
<TD>匹配前面的子表达式零次或一次。例如，"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。</TD></TR>
<TR>
<TD>{n} </TD>
<TD>n 是一个非负整数。匹配确定的 n 次。例如，'o{2}' 不能匹配 "Bob" 中的 'o'，但是能匹配 "food" 中的两个 o。</TD></TR>
<TR>
<TD>{n,} </TD>
<TD>n 是一个非负整数。至少匹配n 次。例如，'o{2,}' 不能匹配 "Bob" 中的 'o'，但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。</TD></TR>
<TR>
<TD>{n,m} </TD>
<TD>m 和 n 均为非负整数，其中n <= m。最少匹配 n 次且最多匹配 m 次。例如，"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。</TD></TR>
<TR>
<TD>? </TD>
<TD>当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时，匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串，而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如，对于字符串 "oooo"，'o+?' 将匹配单个 "o"，而 'o+' 将匹配所有 'o'。</TD></TR>
<TR>
<TD>. </TD>
<TD>匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符，请使用象 '[.\n]' 的模式。</TD></TR>
<TR>
<TD>(pattern) </TD>
<TD>匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到，在VBScript 中使用 SubMatches 集合，在JScript 中则使用 $0…$9 属性。要匹配圆括号字符，请使用 '\(' 或 '\)'。</TD></TR>
<TR>
<TD>(?:pattern) </TD>
<TD>匹配 pattern 但不获取匹配结果，也就是说这是一个非获取匹配，不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如， 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。</TD></TR>
<TR>
<TD>(?=pattern) </TD>
<TD>正向预查，在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如，'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ，但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始。</TD></TR>
<TR>
<TD>(?!pattern) </TD>
<TD>负向预查，在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows"，但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始</TD></TR>
<TR>
<TD>x|y </TD>
<TD>匹配 x 或 y。例如，'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。</TD></TR>
<TR>
<TD>[xyz] </TD>
<TD>字符集合。匹配所包含的任意一个字符。例如， '[abc]' 可以匹配 "plain" 中的 'a'。</TD></TR>
<TR>
<TD>[^xyz] </TD>
<TD>负值字符集合。匹配未包含的任意字符。例如， '[^abc]' 可以匹配 "plain" 中的'p'。</TD></TR>
<TR>
<TD>[a-z] </TD>
<TD>字符范围。匹配指定范围内的任意字符。例如，'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。</TD></TR>
<TR>
<TD>[^a-z] </TD>
<TD>负值字符范围。匹配任何不在指定范围内的任意字符。例如，'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。</TD></TR>
<TR>
<TD>\b </TD>
<TD>匹配一个单词边界，也就是指单词和空格间的位置。例如， 'er\b' 可以匹配"never" 中的 'er'，但不能匹配 "verb" 中的 'er'。</TD></TR>
<TR>
<TD>\B </TD>
<TD>匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er'，但不能匹配 "never" 中的 'er'。</TD></TR>
<TR>
<TD>\cx </TD>
<TD>匹配由 x 指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 'c' 字符。</TD></TR>
<TR>
<TD>\d </TD>
<TD>匹配一个数字字符。等价于 [0-9]。</TD></TR>
<TR>
<TD>\D </TD>
<TD>匹配一个非数字字符。等价于 [^0-9]。</TD></TR>
<TR>
<TD>\f </TD>
<TD>匹配一个换页符。等价于 \x0c 和 \cL。</TD></TR>
<TR>
<TD>\n </TD>
<TD>匹配一个换行符。等价于 \x0a 和 \cJ。</TD></TR>
<TR>
<TD>\r </TD>
<TD>匹配一个回车符。等价于 \x0d 和 \cM。</TD></TR>
<TR>
<TD>\s </TD>
<TD>匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。</TD></TR>
<TR>
<TD>\S </TD>
<TD>匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。</TD></TR>
<TR>
<TD>\t </TD>
<TD>匹配一个制表符。等价于 \x09 和 \cI。</TD></TR>
<TR>
<TD>\v </TD>
<TD>匹配一个垂直制表符。等价于 \x0b 和 \cK。</TD></TR>
<TR>
<TD>\w </TD>
<TD>匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。</TD></TR>
<TR>
<TD>\W </TD>
<TD>匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。</TD></TR>
<TR>
<TD>\xn </TD>
<TD>匹配 n，其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如，'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。.</TD></TR>
<TR>
<TD>\num </TD>
<TD>匹配 num，其中 num 是一个正整数。对所获取的匹配的引用。例如，'(.)\1' 匹配两个连续的相同字符。</TD></TR>
<TR>
<TD>\n </TD>
<TD>标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式，则 n 为向后引用。否则，如果 n 为八进制数字 (0-7)，则 n 为一个八进制转义值。</TD></TR>
<TR>
<TD>\nm </TD>
<TD>标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式，则 nm 为向后引用。如果 \nm 之前至少有 n 个获取，则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足，若 n 和 m 均为八进制数字 (0-7)，则 \nm 将匹配八进制转义值 nm。</TD></TR>
<TR>
<TD>\nml </TD>
<TD>如果 n 为八进制数字 (0-3)，且 m 和 l 均为八进制数字 (0-7)，则匹配八进制转义值 nml。</TD></TR>
<TR>
<TD>\un </TD>
<TD>匹配 n，其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如， \u00A9 匹配版权符号 (?)。</TD></TR></TBODY></TABLE>
<P></P>
<P>
<H3>部分例子</H3><BR>
<TABLE border=1>
<TBODY>
<TR>
<TD>正则表达式</TD>
<TD>说明</TD></TR>
<TR>
<TD>/\b([a-z]+) \1\b/gi</TD>
<TD>一个单词连续出现的位置</TD></TR>
<TR>
<TD>/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/ </TD>
<TD>将一个URL解析为协议、域、端口及相对路径</TD></TR>
<TR>
<TD>/^(?:Chapter|Section) [1-9][0-9]{0,1}$/</TD>
<TD>定位章节的位置</TD></TR>
<TR>
<TD>/[-a-z]/</TD>
<TD>A至z共26个字母再加一个-号。</TD></TR>
<TR>
<TD>/ter\b/</TD>
<TD>可匹配chapter，而不能terminal</TD></TR>
<TR>
<TD>/\Bapt/</TD>
<TD>可匹配chapter，而不能aptitude</TD></TR>
<TR>
<TD>/Windows(?=95 |98 |NT )/</TD>
<TD>可匹配Windows95或Windows98或WindowsNT,当找到一个匹配后，从Windows后面开始进行下一次的检索匹配。</TD></TR></TBODY></TABLE>
<P></P>
<P>参考文献：<BR>正则表达式<BR>http://www.soulogic.com/code/doc/RegularExpressions/</P><img src ="http://www.blogjava.net/qq13367612/aggbug/16021.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-14 22:22 <a href="http://www.blogjava.net/qq13367612/articles/16021.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式使用详解</title><link>http://www.blogjava.net/qq13367612/articles/16022.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 14 Oct 2005 14:19:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16022.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16022.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16022.html</trackback:ping><description><![CDATA[如果我们问那些UNIX系统的爱好者他们最喜欢什么，答案除了稳定的系统和可以远程启动之外，十有八九的人会提到正则表达式；如果我们再问他们最头痛的是什么，可能除了复杂的进程控制和安装过程之外，还会是正则表达式。那么正则表达式到底是什么？如何才能真正的掌握正则表达式并正确的加以灵活运用？本文将就此展开介绍，希望能够对那些渴望了解和掌握正则表达式的读者有所助益。
<P>入门简介</P>
<P>　　简单的说，正则表达式是一种可以用于模式匹配和替换的强有力的工具。我们可以在几乎所有的基于UNIX系统的工具中找到正则表达式的身影，例如，vi编辑器，Perl或PHP脚本语言，以及awk或sed shell程序等。此外，象JavaScript这种客户端的脚本语言也提供了对正则表达式的支持。由此可见，正则表达式已经超出了某种语言或某个系统的局限，成为人们广为接受的概念和功能。</P>
<P>　　正则表达式可以让用户通过使用一系列的特殊字符构建匹配模式，然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较，根据比较对象中是否包含匹配模式，执行相应的程序。</P>
<P>　　举例来说，正则表达式的一个最为普遍的应用就是用于验证用户在线输入的邮件地址的格式是否正确。如果通过正则表达式验证用户邮件地址的格式正确，用户所填写的表单信息将会被正常处理；反之，如果用户输入的邮件地址与正则表达的模式不匹配，将会弹出提示信息，要求用户重新输入正确的邮件地址。由此可见正则表达式在WEB应用的逻辑判断中具有举足轻重的作用。</P>
<P>基本语法</P>
<P>　　在对正则表达式的功能和作用有了初步的了解之后，我们就来具体看一下正则表达式的语法格式。</P>
<P>　　正则表达式的形式一般如下：</P>
<P>　　/love/</P>
<P>　　其中位于“/”定界符之间的部分就是将要在目标对象中进行匹配的模式。用户只要把希望查找匹配对象的模式内容放入“/”定界符之间即可。为了能够使用户更加灵活的定制模式内容，正则表达式提供了专门的“元字符”。所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符，可以用来规定其前导字符（即位于元字符前面的字符）在目标对象中的出现模式。</P>
<P>　　较为常用的元字符包括： “+”， “*”，以及 “?”。其中，“+”元字符规定其前导字符必须在目标对象中连续出现一次或多次，“*”元字符规定其前导字符必须在目标对象中出现零次或连续多次，而“?”元字符规定其前导对象必须在目标对象中连续出现零次或一次。</P>
<P>　　下面，就让我们来看一下正则表达式元字符的具体应用。</P>
<P>　　/fo+/</P>
<P>　　因为上述正则表达式中包含“+”元字符，表示可以与目标对象中的 “fool”, “fo”, 或者 “football”等在字母f后面连续出现一个或多个字母o的字符串相匹配。</P>
<P>　　/eg*/</P>
<P>　　因为上述正则表达式中包含“*”元字符，表示可以与目标对象中的 “easy”, “ego”, 或者 “egg”等在字母e后面连续出现零个或多个字母g的字符串相匹配。</P>
<P>　　/Wil?/</P>
<P>　　因为上述正则表达式中包含“？”元字符，表示可以与目标对象中的 “Will”, 或者 “Wilson”,等在字母i后面连续出现零个或一个字母l的字符串相匹配。</P>
<P>　　除了元字符之外，用户还可以精确指定模式在匹配对象中出现的频率。例如，</P>
<P>　　/jim{2,6}/</P>
<P>　　上述正则表达式规定字符m可以在匹配对象中连续出现2-6次，因此，上述正则表达式可以同jimmy或jimmmmmy等字符串相匹配。</P>
<P>　　在对如何使用正则表达式有了初步了解之后，我们来看一下其它几个重要的元字符的使用方式。</P>
<P>　　s：用于匹配单个空格符，包括tab键和换行符；</P>
<P>　　S：用于匹配除单个空格符之外的所有字符；</P>
<P>　　d：用于匹配从0到9的数字；</P>
<P>　　w：用于匹配字母，数字或下划线字符；</P>
<P>　　W：用于匹配所有与w不匹配的字符；</P>
<P>　　. ：用于匹配除换行符之外的所有字符。</P>
<P>　　（说明：我们可以把s和S以及w和W看作互为逆运算）<BR>下面，我们就通过实例看一下如何在正则表达式中使用上述元字符。</P>
<P>　　/s+/</P>
<P>　　上述正则表达式可以用于匹配目标对象中的一个或多个空格字符。</P>
<P>　　/d000/</P>
<P>　　如果我们手中有一份复杂的财务报表，那么我们可以通过上述正则表达式轻而易举的查找到所有总额达千元的款项。</P>
<P>　　除了我们以上所介绍的元字符之外，正则表达式中还具有另外一种较为独特的专用字符，即定位符。定位符用于规定匹配模式在目标对象中的出现位置。</P>
<P>　　较为常用的定位符包括： “^”, “$”, “” 以及 “B”。其中，“^”定位符规定匹配模式必须出现在目标字符串的开头，“$”定位符规定匹配模式必须出现在目标对象的结尾，定位符规定匹配模式必须出现在目标字符串的开头或结尾的两个边界之一，而“B”定位符则规定匹配对象必须位于目标字符串的开头和结尾两个边界之内，即匹配对象既不能作为目标字符串的开头，也不能作为目标字符串的结尾。同样，我们也可以把“^”和“$”以及“”和“B”看作是互为逆运算的两组定位符。举例来说：</P>
<P>　　/^hell/</P>
<P>　　因为上述正则表达式中包含“^”定位符，所以可以与目标对象中以 “hell”, “hello”或 “hellhound”开头的字符串相匹配。</P>
<P>　　/ar$/</P>
<P>　　因为上述正则表达式中包含“$”定位符，所以可以与目标对象中以 “car”, “bar”或 “ar” 结尾的字符串相匹配。</P>
<P>　　/bom/</P>
<P>　　因为上述正则表达式模式以“”定位符开头，所以可以与目标对象中以 “bomb”, 或 “bom”开头的字符串相匹配。</P>
<P>　　/man/</P>
<P>　　因为上述正则表达式模式以“”定位符结尾，所以可以与目标对象中以 “human”, “woman”或 “man”结尾的字符串相匹配。</P>
<P>　　为了能够方便用户更加灵活的设定匹配模式，正则表达式允许使用者在匹配模式中指定某一个范围而不局限于具体的字符。例如：</P>
<P>　　/[A-Z]/</P>
<P>　　上述正则表达式将会与从A到Z范围内任何一个大写字母相匹配。</P>
<P>　　/[a-z]/</P>
<P>　　上述正则表达式将会与从a到z范围内任何一个小写字母相匹配。</P>
<P>　　/[0-9]/</P>
<P>　　上述正则表达式将会与从0到9范围内任何一个数字相匹配。</P>
<P>　　/([a-z][A-Z][0-9])+/</P>
<P>　　上述正则表达式将会与任何由字母和数字组成的字符串，如 “aB0” 等相匹配。这里需要提醒用户注意的一点就是可以在正则表达式中使用 “()” 把字符串组合在一起。“()”符号包含的内容必须同时出现在目标对象中。因此，上述正则表达式将无法与诸如 “abc”等的字符串匹配，因为“abc”中的最后一个字符为字母而非数字。</P>
<P>　　如果我们希望在正则表达式中实现类似编程逻辑中的“或”运算，在多个不同的模式中任选一个进行匹配的话，可以使用管道符 “|”。例如：</P>
<P>　　/to|too|2/</P>
<P>　　上述正则表达式将会与目标对象中的 “to”, “too”, 或 “2” 相匹配。</P>
<P>　　正则表达式中还有一个较为常用的运算符，即否定符 “[^]”。与我们前文所介绍的定位符 “^” 不同，否定符 “[^]”规定目标对象中不能存在模式中所规定的字符串。例如：</P>
<P>　　/[^A-C]/</P>
<P>　　上述字符串将会与目标对象中除A，B，和C之外的任何字符相匹配。一般来说，当“^”出现在 “[]”内时就被视做否定运算符；而当“^”位于“[]”之外，或没有“[]”时，则应当被视做定位符。</P>
<P>　　最后，当用户需要在正则表达式的模式中加入元字符，并查找其匹配对象时，可以使用转义符“”。例如：</P>
<P>　　/Th*/</P>
<P>　　上述正则表达式将会与目标对象中的“Th*”而非“The”等相匹配。<BR><STRONG>使用实例</STRONG></P>
<P>　　在对正则表达式有了较为全面的了解之后，我们就来看一下如何在Perl，PHP，以及JavaScript中使用正则表达式。</P>
<P>　　通常，Perl中正则表达式的使用格式如下：</P>
<P>　　operator / regular-expression / string-to-replace / modifiers</P>
<P>　　运算符一项可以是m或s，分别代表匹配运算和替换运算。</P>
<P>　　其中，正则表达式一项是将要进行匹配或替换操作的模式，可以由任意字符，元字符，或定位符等组成。替换字符串一项是使用s运算符时，对查找到的模式匹配对象进行替换的字符串。最后的参数项用来控制不同的匹配或替换方式。例如：</P>
<P>　　s/geed/good/</P>
<P>　　将会在目标对象中查找第一个出现的geed字串，并将其替换为good。如果我们希望在目标对象的全局范围内执行多次查找—替换操作的话，可以使用参数 “g”，即s/love/lust/g。</P>
<P>　　此外，如果我们不需要限制匹配的大小写形式的话，可以使用参数 “i ”。例如，</P>
<P>　　m/JewEL/i</P>
<P>　　上述正则表达式将会与目标对象中的jewel，Jewel，或JEWEL相匹配。</P>
<P>　　在Perl中，使用专门的运算符“=~”指定正则表达式的匹配对象。例如：</P>
<P>　　$flag =~ s/abc/ABC/</P>
<P>　　上述正则表达式将会把变量$flag中的字串abc替换为ABC。</P>
<P>　　下面，我们就在Perl程序中加入正则表达式，验证用户邮件地址格式的有效性。代码如下：</P>
<P>　　#!/usr/bin/perl<BR>　　# get input<BR>　　print “What's your email address? ”;<BR>　　$email = <><BR>　　chomp($email);<BR>　　# match and display result<BR>　　if($email =~ /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/)<BR>　　{<BR>　　print(“Your email address is correct! ”);<BR>　　}<BR>　　else<BR>　　　{<BR>　　　　print(“Please try again! ”);<BR>　　　}<BR>如果用户更偏爱PHP的话，可以使用ereg()函数进行模式匹配操作。ereg()函数的使用格式如下：<BR>　　 ereg(pattern, string)</P>
<P>　　其中，pattern代表正则表达式的模式，而string则是执行查找替换操作的目标对象。同样是验证邮件地址，使用PHP编写的程序代码如下：</P>
<P>　　<?php<BR>　　 if (ereg(“^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+”,$email))<BR>　　　 { echo “Your email address is correct!”;}<BR>　　 else<BR>　　　 { echo “Please try again!”;}<BR>　　 ?><BR>　　最后，我们在来看一下JavaScript。JavaScript 1.2中带有一个功能强大的RegExp()对象，可以用来进行正则表达式的匹配操作。其中的test()方法可以检验目标对象中是否包含匹配模式，并相应的返回true或false。</P>
<P>　　我们可以使用JavaScript编写以下脚本，验证用户输入的邮件地址的有效性。</P>
<P>　　<html><BR>　　 <head><BR>　　　 < language="Javascript1.2"><BR>　　　　 <!-- start hiding<BR>　　　　 function verifyAddress(obj)<BR>　　　　　{<BR>　　　　　　var email = obj.email.value;<BR>　　　　　　var pattern = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/;<BR>　　　　　　flag = pattern.test(email);<BR>　　　　　　if(flag)<BR>　　　　　　{<BR>　　　　　　　alert(“Your email address is correct!”);<BR>　　　　　　　return true;<BR>　　　　　　}<BR>　　　　　　else<BR>　　　　　　　{<BR>　　　　　　　　alert(“Please try again!”);<BR>　　　　　　　　return false;<BR>　　　　　　　 }<BR>　　　　　 }<BR>　　　　 // stop hiding --><BR>　　　 </script><BR>　　　</head><BR>　　 <body><BR>　　 <form onSubmit="return verifyAddress(this);"><BR>　　　<input name="email" type="text"><BR>　　　<input type="submit" value="提交"><BR>　　　</form><BR>　　</body><BR>　</html></P><img src ="http://www.blogjava.net/qq13367612/aggbug/16022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-14 22:19 <a href="http://www.blogjava.net/qq13367612/articles/16022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>工作流现状</title><link>http://www.blogjava.net/qq13367612/articles/16024.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 13 Oct 2005 08:47:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16024.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16024.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16024.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16024.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16024.html</trackback:ping><description><![CDATA[<FONT size=7><FONT face=宋体><SPAN class=style1><SPAN class=style2><STRONG>工作流</STRONG></SPAN></SPAN><SPAN class=style3>现状</SPAN></FONT></FONT> （<A href="http://jbpm.org/2/state.of.workflow.html" target=_blank>原文</A>） 
<HR>
<SPAN style="WIDTH: 100%; TEXT-ALIGN: right"><B>作者<A href="http://jbpm.org/2/state.of.workflow.html#tombaeyens">Tom Baeyens </A>翻译<A href="http://www.joinwork.net/document/The%20State%20of%20Workflow2.htm#dinghong">dinghong</A></B></SPAN> <A href="http://www.joinwork.net/"><!-- =================================================================================== --></A>
<H1>前言</H1>
<P><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;如果数据库系统（ database systems）像受人尊敬的智者讲述的条理清晰的故事，那么<STRONG>工作流</STRONG>（<STRONG>workflow</STRONG>）就像一群乳臭未干的小子在大谈各自的“哲理”。之所以这样讲，我是想指出，<STRONG>工作流</STRONG>系统 （<STRONG>workflow</STRONG> management systems）还处于技术发展曲线（ <A href="http://www.ayeconference.com/wiki/scribble.cgi?read=HypeCycle">technology hype curve</A>）上的初级阶段。在这个领域我们将面临一个激动人心的阶段。 为了描述这一点，可以将<STRONG>工作流</STRONG>和关系数据库系统（RDBMS）做一个对比。当在软件开发团队中谈论RDBMS时，大部分人会有一个清晰的概念，在你和他们交流的时候，人们会通过轻微的点头表示认可或理解你所说的。可当使用工作流术语讨论<STRONG>工作流</STRONG>时，他们会摇头表示不同意，因为每个人对<STRONG>工作流</STRONG>术语都有不同的理解。</SPAN></P>
<P>
<CENTER style="MARGIN-BOTTOM: 10px"><IMG alt="the technology hype graph" src="http://www.joinwork.net/document/The%20State%20of%20Workflow.files/hype-graph.gif" border=1><BR><I>Figure 1: Workflow vs. RDBMS positioned in the hype-curve</I> </CENTER>
<P></P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;导致形成这种状况的原因之一，是在<STRONG>工作流</STRONG>中使用了过多的概念。在这个领域中的大量规范和工具没有一个是相似的。当然，它们相互之间有重叠并且会相互参考引证。<BR>&nbsp;&nbsp;&nbsp;&nbsp;在介绍<STRONG>工作流</STRONG>时有一个话题必须包括，那就是<STRONG>工作流</STRONG>和<STRONG>业务流程管理</STRONG>（BPM）的关系。术语“<STRONG>工作流</STRONG>”通常描述人与计算机系统的一系列相关交互。在开发人员中，<STRONG>工作流</STRONG>经常被提及。有时，<STRONG>工作流</STRONG>的意思是指一些不同的UI界面。业务流程管理的范围比较广，相比之下<STRONG>工作流</STRONG>多半局限于技术领域。业务流程管理还从管理人员的角度涉及了非技术问题，比如分析、组织的效率。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;在本文中，我首先解释什么是<STRONG>工作流</STRONG>管理系统，然后介绍业务流程管理的优点。接下来描述一下为什么<STRONG>工作流</STRONG>市场乍看起来如此混乱。本文给出的主要结论是：选择<STRONG>工作流</STRONG>系统是想用<STRONG>工作流</STRONG>系统的公司，将要面对的最困难的事情。为此，本文的核心部分描述了一个流程定义（process definition）的四个层次，为你选择<STRONG>工作流</STRONG>提供一个基础。本文还用中立的语言描述了<STRONG>工作流</STRONG>和BPM的通用概念。最后，给出了一些规范和工具的指导性描述。</P><SPAN class=text2><!-- =================================================================================== --></SPAN>
<H1>什么是<STRONG>工作流</STRONG>管理系统（WFMS）</H1>
<H2>定义</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>工作流</STRONG>系统是以规格化的流程描述作为输入的软件组件,它维护流程的运行状态,并在人和应用之间分派活动。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;为了后面的描述，我们先定义一些基本的术语：流程定义（process definition）和流程实例（process instance）. <U>一个流程定义</U>是一个业务流程或过程的规格化描述。一个<U>流程实例</U>是流程定义的一个运行实体。 都目前为止，概念还比较清晰是不是？但当再深入一步时，我们就要小心使用文字了。如何阐述流程中的步骤，现在还没有一个统一的方式。这是各种<STRONG>工作流</STRONG>规范和工具之间主要的分歧。</P>
<TABLE cellSpacing=25 cellPadding=8 border=0>
<TBODY>
<TR>
<TD style="BORDER-RIGHT: black 1px dashed; BORDER-TOP: black 1px dashed; BORDER-LEFT: black 1px dashed; BORDER-BOTTOM: black 1px dashed" vAlign=top width="100%">
<P id=activityshouldbebanned><B>为什么应当禁止使用术语“活动（activity）”...</B><BR><SPAN class=note><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>流程定义通常用一些活动表述。我认为这是导致工作流领域所有混乱的主要原因。我告诉你为什么：因为术语“活动”混淆了状态（state）和动作（action）之间的差异。在流程中，<U>状态</U> (或者说等待状态)代表了一种对外部参与者（actor）的依赖。在流程运行时，这意味着流程引擎必须等待，直到外部参与者通知工作流管理系统指定的状态完成了。比如，等待可进一步运行的认可。<U>动作</U>是在流程运行过程中，工作流系统为响应指定事件（event）运行的一段程序逻辑（programming logic）。当流程运行过程中指定的事件发生时，工作流系统启动并执行这些动作。比如，当状态分配给一个参与者时，发一封Email。你也能看出，状态和动作是如此不同，因此使用同样的术语去描述这些概念是一个坏习惯。我的建议是避免使用术语“活动”，使用“状态”或者“动作”代替它。</SPAN></P><!-- END OF BORDER --></TD></TR></TBODY></TABLE>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>工作流系统</STRONG>另一个重要的职责是维护每一个流程运行的上下文信息。 <U>流程上下文变量（process context variable）</U> ，或简称变量，是与流程实例相关的变量。如，休假申请的开始日期、数据库中一条记录的键值、文档管理系统中一篇文档的索引等。通常在流程定义中声明这些变量，然后在流程实例生成时，这些流程变量被实例化。所有成熟的<STRONG>工作流管理系统</STRONG>都支持定制的变量类型。</P>
<H2>目标领域（Target usage）</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;使用<STRONG>工作流管理系统</STRONG>的目的之一是作为企业应用系统集成（EAI）的平台。在当前大部分企业级IT架构中，各种各样的异构（heterogeneous）应用和数据库运行在企业内网中。在这些系统被应用到组织时，都有一个清晰的目标。例如，客户管理、文档管理、供应链、订单、支付、资源计划等等。让我们称这些系统为专门应用（ dedicated applications）。每一个专门应用都包含它们所支持业务流程的领域知识。这些专门应用中的自动化流程，被拼装到企业中更大的非自动化流程中。每当一个这样的专门应用安装并投入使用，都会带来涉及其他多个应用的新功能需求。企业应用系统集成（EAI）就是通过使用多个专门应用满足软件新需求的方法。有时，这只需要在两个应用之间提供数据通讯的通道。专门应用将很多业务流程硬编码在软件中。可以这么说，在你购买专门应用时，你是购买了一组固定的自动化业务流程。而<STRONG>工作流管理系统</STRONG>是不必事先知道问题域的相关信息的。<STRONG>工作流系统</STRONG>将业务流程描述作为输入并管理流程实例的执行，这使得它比专门应用更灵活（当然你也要花精力编写业务流程的规格化描述）。这就是为什么说<STRONG>工作流系统</STRONG>和专门系统是相互补充的。<STRONG>工作流系统</STRONG>可以用来管理全局的业务流程。如果专门应用支持你所需要的业务流程，那么使用专门应用。在此讨论的<STRONG>工作流系统</STRONG>的第一种使用方式就是：结合所有的专门应用，使用<STRONG>工作流系统</STRONG>构建一个EAI平台。</P>
<P class=text2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><STRONG>工作流系统</STRONG>能够发挥很大价值的第二个使用方式是：协助涉及多人相关任务<STRONG>工作流</STRONG>软件的开发。为了达到这个目的，大部分<STRONG>工作流系统</STRONG>都有一个方便的机制，来生成执行任务的表单。对于专注于<A href="http://www.iso.ch/iso/en/iso9000-14000/iso9000/iso9000index.html">ISO</A> 或者<A href="http://www.sei.cmu.edu/cmm/cmm.html">CMM</A>认证的组织，采用这种方式使用<STRONG>工作流系统</STRONG>能够显著提高生产率。 不用将过程用文字的形式写在纸上，<STRONG>工作流系统</STRONG>使你通过流程定义建模实现过程的自动化（如使用基于Web的应用）。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>工作流系统</STRONG>的第三种使用方式是：将<STRONG>工作流引擎</STRONG>嵌入到其他应用中。在前面我们谈到，专门应用将指定问题域相关的业务流程固化在软件中。开发专门应用的公司也可以将<STRONG>工作流引擎</STRONG>嵌入到他们的软件中。在这里，<STRONG>工作流引擎</STRONG>只是作为一个软件组件，对于应用的最终用户是不可见的。将<STRONG>工作流引擎</STRONG>嵌入到应用中的主要原因是为了重用（不重复发明轮子）和应用软件的可维护性。</P><!-- =================================================================================== -->
<H1>The case for workflow</H1>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;对于引入<STRONG>工作流</STRONG>的组织，能够在软件开发和业务两个层次受益。</P>
<H2>方便开发</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>工作流管理系统</STRONG>能够简化企业级软件开发甚至维护。 </P>
<UL>
<LI><SPAN class=text2><B>降低开发风险</B> - 通过使用状态和动作这样的术语，业务分析师和开发人员使用同一种语言交谈。这样开发人员就不必将用户需求转化成软件设计了。 </SPAN>
<LI><SPAN class=text2><B>实现的集中统一</B> -业务流程经常变化，使用<STRONG>工作流</STRONG>系统的最大好处是：业务流程的实现代码，不再是散落在各种各样的系统中 。 </SPAN>
<LI class=text2><B>加快应用开发</B> - 你的软件不用再关注流程的参与者，开发起来更快，代码更容易维护。 </LI></UL>
<H2>业务流程管理 (BPM)</H2>
<P class=text2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>在自动化业务流程之前，分析并将它们规格化是一件艰苦但会有很好回报的工作。<A href="http://www.e-workflow.org/">e-workflow.org</A>对于分析流程能够带了的益处有不错的阐述：</P>
<UL>
<LI><SPAN class=text2><B>提高效率</B> - 许多流程在自动化过程中会去除一些不必要的步骤 </SPAN>
<LI class=text2><B>较好的流程控制</B> - 通过标准的工作方法和跟踪审计，提高了业务流程的管理 
<LI><SPAN class=text2><B>改进客户服务</B> - 因为流程的一致性，提高了对客户响应的可预见性 </SPAN>
<LI><SPAN class=text2><B>灵活</B> - 跨越流程的软件控制，使流程可以按照业务的需要重新设计。 </SPAN>
<LI class=text2><B>业务流程改进</B> - 对流程的关注，使它们趋向于流畅和简单 </LI></UL>
<P class=text2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>我认为他们还遗漏了一个使用<STRONG>工作流</STRONG>系统最重要的因数：提高对迭代开发的支持。如果软件中业务流程部分不容易更改，组织就会花很大的精力在开发前的业务流程分析中，希望一次成功。但可悲的是，在任何软件项目开发中，这都很少能实现。<STRONG>工作流</STRONG>系统使得新业务流程很容易部署，业务流程相关的软件可以一种迭代的方式开发，因此使用<STRONG>工作流</STRONG>系统使开发更有效、风险更低。</P>
<H2>缺失的一环（Missing link）</H2>
<H2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;我确实认为<STRONG>工作流</STRONG>系统是企业应用开发中缺失的一环。将企业业务流程逻辑在企业级软件中实现的缺省方式是分散的。这意味着业务流程逻辑散布在各种系统中，如EJB、数据库触发器、消息代理等等。这样得到的软件难于维护，结果，企业只能将改变业务流程软件作为最后的选择。他们经常能够做的是，改变流程以适应软件。上述问题也适用于采用大型外部ERP软件包的企业。进一步，假设我们认识到这个问题，并打算将一个流程相关的代码都集中起来。对于一个流程来说这很不错，但当你要实现多个流程时，你会看到管理状态和流程变量的代码被到处复制。最后，我们会整理这些代码放到一个集中的库中。好，...这就是一个<STRONG>工作流管理系统</STRONG>了，不用费心自己来实现，你可以从本文后面的列表中选择一个</SPAN><SPAN class=text2>。</SPAN> </H2>
<H1>A closer look</H1>
<H2>WFMS interfaces</H2>
<P class=text2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>一个<STRONG>工作流管理系统</STRONG>以流程定义作为输入。在这里，可以将流程定义看作UML活动图、UML状态图或者有限状态机。在提交一张费用单据、申请休假、要求一个报价、...之后，<STRONG>工作流</STRONG>系统负责维护这些流程定义的执行状态和上下文。为此，需要通知<STRONG>工作流</STRONG>系统状态的变化。运行流程的状态变化可以记录下来，以备监控管理。</P>
<P>
<CENTER><IMG src="http://www.joinwork.net/document/The%20State%20of%20Workflow.files/interfaces.gif" border=1><BR><I>Figure 2: Interfaces of a WFMS</I> </CENTER>
<P></P>
<P>
<UL>
<LI><SPAN class=text2><B>定义</B>&nbsp;&nbsp;&nbsp;<STRONG>工作流</STRONG>系统的定义接口使流程开发人员能够部署流程定义。注意，这里的“流程开发人员”可以是业务分析师和软件开发人员的组合。</SPAN> <!-- START OF BORDER -->
<TABLE cellSpacing=25 cellPadding=8 border=0>
<TBODY>
<TR>
<TD style="BORDER-RIGHT: black 1px dashed; BORDER-TOP: black 1px dashed; BORDER-LEFT: black 1px dashed; BORDER-BOTTOM: black 1px dashed" vAlign=top width="100%">
<P width=100%><B>圈套（Pitfall）</B><BR><SPAN class=note>许多<STRONG>工作流管理系统</STRONG>的开发商想使你相信，通过使用他们的图形化流程开发工具，只要业务分析师就可以生成流程定义。这种幻想源于“编程很难”这样的事实。开发商的销售人员喜欢说“看，你不用写一行代码”。不用写代码是好事，可大部分开发商在这点上走的太远，忽略了在某些场合提供一种将代码集成到流程定义中的机制是很适合的。在将<STRONG>工作流</STRONG>系统作为EAI平台时，必须在流程中集成代码。开发流程定义需要业务分析师和软件开发人员的合作。一个好的图形流程设计工具应该能够支持这种合作。</SPAN></P><!-- END OF BORDER --></TD></TR></TBODY></TABLE>
<LI><SPAN class=text2><B>执行</B>&nbsp;&nbsp;&nbsp;执行接口使用户和系统可以操作流程实例。流程实例是流程定义的执行。流程定义的控制流通过状态机描述。执行接口的两个主要方法是启动一个流程实例和通知<STRONG>工作流</STRONG>系统一个状态结束了。 </SPAN>
<LI><SPAN class=text2><B>应用</B>&nbsp;&nbsp;&nbsp;应用接口代表了由<STRONG>工作流</STRONG>系统发起的<STRONG>工作流</STRONG>系统和外部系统之间的交互。当一个用户或系统操作一个流程实例的运行时，会生成一些事件（如一个迁移的执行）。流程定义中可以指定一段响应一个事件的可执行代码逻辑，这段代码和组织内外部的其他系统打交道。 </SPAN>
<LI class=text2><B>监控</B>&nbsp;&nbsp;&nbsp;管理人员通过监控接口获得流程运行的确切数据。有时，运行日志也可用于审计。 </LI></UL><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;这些是WfMC参考模型（<A href="http://www.wfmc.org/standards/model.htm">reference model of the WfMC</A>）中定义的五个接口中的四个。 </SPAN>
<H2>流程定义的四个层次</H2>
<H2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;在下面这部分，我尝试回答这样的问题“什么是流程定义包括的内容？”。这是从各种规范和工具所使用模型的原则和概念中总结得来的，反映了大部分模型中通用的基本思想。流程定义的内容可以分为四个不同的层次：状态（state）、上下文（context）、程序逻辑（programming logic）和用户界面（UI）。</SPAN> </H2>
<H2>状态层</H2>
<P class=text2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>所有状态和控制流的表述，都属于业务流程的状态层。标准编程语言中的控制流来源于Von Neuman体系。控制流定义了必须被执行的指令的顺序，控制流由我们书写的命令、if语句、循环语句等确定。在业务流程中的控制流基本与此一致。但在业务流程中不是使用命令而是使用状态作为基本元素。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;在流程中，<U>状态</U> (或者说等待状态)代表了一种对外部参与者（actor）的依赖。状态的意思就像“现在X系统或某某人必须作某些事，在此等待直到参与者通知这些任务已完成”。状态定义了一种对外部提供结果的依赖。状态典型的例子是批准步骤（step）。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;流程定义中的状态也指定了执行依赖于哪个参与者。在活动图中，泳道（swimlanes）的标注代表这些参与者的名字。<STRONG>工作流</STRONG>系统使用这些信息构建任务列表，这是一般<STRONG>工作流</STRONG>系统都有的功能。如前所述，参与者可以是人也可以是系统。对于需要人参与的状态，<STRONG>工作流</STRONG>系统必须在运行时计算出具体的个人。这样的计算使<STRONG>工作流</STRONG>系统必须依赖于组织结构信息。关于这方面的一篇非常有趣的文章是在<A href="http://jbpm.org/2/state.of.workflow.html#furtherreading">further reading section</A>提到的“<STRONG>工作流</STRONG>应用中的组织管理”（ 'Organizational Management in Workflow Applications'）。</P>
<P><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;流程定义的控制流包含一组状态和它们之间的关系。状态之间的逻辑关系描述了哪些执行路径可以同时执行，那些不可以。同步执行路径用分叉（forks）和联合（joins）建模，异步执行路径用判断（decisions）和合并（ merges）建模。注意在大多数模型中，在每个状态之前都有一个隐式合并</SPAN>。</P>
<P><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;UML活动图经常被用来做业务流程建模。作为一种直观和通用的表达，活动图在图形表述上有一个主要问题，就是没有区分状态和动作，它们都用活动来表示。缺少这种区分（导致状态概念的缺失）是学术派对UML活动图的主要批评。UML活动图的第二个问题是在UML2.0版中引入的。当多个迁移（transitions）到达一个活动时，以前的版本规定这是一个缺省合并（merge），在2.0版中规定这是一个需要同步的缺省联合（join）。在我看来，UML活动图的图形部分仍旧可以用来对业务流程状态层次建模，只要使用时对两条构建语义作如下的变化：</SPAN> 
<OL>
<LI><SPAN class=text2>在用图形表述业务流程时，只建模状态层（状态和控制流），不要包括动作。这意味着图形中的矩形都是状态而不是活动 </SPAN>
<LI class=text2>如果多个迁移到达一个状态，缺省定义为不需要同步的合并（merges） </LI></OL>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;在流程运行过程中，<STRONG>工作流</STRONG>系统用一个令牌（token）作为指针跟踪流程的状态。这相当于Von Neuman体系中的程序计数器。当令牌到达一个状态时，它被分配给<STRONG>工作流</STRONG>系统等待的外部参与者。外部参与者可以是个人、组织或者计算机系统。我们定义流程运行的执行人或系统为“参与者”（actor）。只有在<STRONG>工作流</STRONG>系统将令牌分配给一个参与者时，才需要访问组织结构信息。<STRONG>工作流</STRONG>系统通过分配令牌构建任务列表。</P>
<H2>上下文层</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;<U>流程上下文变量（process context variable）</U> ，或简称变量，是与流程实例相关的变量。流程开发人员可以使用流程变量存储跨越流程实例整个生命周期的数据。一些<STRONG>工作流管理系统</STRONG>有固定数目的数据类型，另一些你可以定义自己的数据类型。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;注意变量也可以用来存放引用（ references）。一个变量可以引用如数据库中的记录、网络上的文件。什么时候使用引用，取决于使用引用数据的其他应用。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;和流程变量相关的另一个令人感兴趣的方面是：<STRONG>工作流</STRONG>系统如何将数据转化为信息。<STRONG>工作流</STRONG>是用于组织内部跨越各种异构系统实现任务和数据协同的。对于业务流程中人工执行的任务，<STRONG>工作流</STRONG>系统负责从其他相关系统，如SAP、数据库、CRM系统、文档管理系统收集数据。在业务流程的每一个人工步骤，只有相关联的数据项被从异构系统中收集和计算。通过这种方式，从不同系统来的数据被转换并展现为信息。</P>
<H2 id=programminglogic>程序逻辑层</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;如前所述，<U>动作</U>是在流程运行过程中，<STRONG>工作流</STRONG>系统响应指定的事件（event）执行的一段程序逻辑（programming logic）。程序逻辑可以是二进制或源代码形式的、用任何语言或脚本编写的软件。程序逻辑层是所有这些软件片断和关于在什么事件发生时调用它们的信息的组合。程序逻辑的例子包括发Email、通过消息代理发消息、从ERP系统中拿数据和更新数据库。</P>
<H2>用户界面层</H2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;一个参与者通过向流程变量中填充数据的事件，来触发结束一个状态。比如，在请假的例子中，老板提供“同意”或“不同意”数据到流程中。某些<STRONG>工作流</STRONG>系统允许指定哪些数据可以填充到流程中，以及它们如何在流程变量中存储。通过这些信息，可以生成从用户收集信息的UI表单。基于流程定义生成用户提交表单的Web应用例子，可以访问<A href="http://jbpm.org/demo.html">the jBpm online demo</A>。</SPAN><!-- =================================================================================== --> 
<H1><STRONG>工作流</STRONG>全景</H1>
<H2>可执行流程与<STRONG>工作流管理系统</STRONG>的比较（Executional processes versus a WFMS）</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;当前在BPM领域中，关于可执行业务流程的规范有趋向于统一集中的趋势。 XLANG, WSFL 和BPML合并为基于交互（消息交换）的BPEL。BPEL在面向服务体系结构(SOA)的大背景下定义。它的前提条件之一是涉及的服务必须用WSDL声明。BPEL规定了一套XML语法，这套语法可以看作一种编程语言，用来描述包括对WSDL定义的服务调用的控制流。</P>
<P><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;在可执行业务流程和基于状态的<STRONG>工作流管理系统</STRONG>所使用的方法中，我注意到了三点主要的区别：</SPAN> 
<UL>
<LI class=text2><U>基于状态与面向消息</U>：基于状态的<STRONG>工作流</STRONG>系统以状态（或者活动）概念为中心。<STRONG>工作流引擎</STRONG>维护状态并计算从一个状态到另一个状态的迁移。另一方面，像BPEL这样的可执行流程以对输入消息响应的定义为中心。一组这些响应外加其他信息（other bells and whistles）可以看作一个业务流程。这也解释了为什么BPEL可以看作是对基于状态的<STRONG>工作流</STRONG>系统的某些方面的补充。一个响应输入消息的BPEL onMessage事件处理器，可以在<STRONG>工作流</STRONG>状态之间的迁移中执行。 
<LI><SPAN class=text2><U>流程实例ID与消息相关处理</U>：可执行业务流程的复杂性之一来自消息相关性的处理。流程描述的一部分必须说明BPEL引擎如何从输入消息中确定具体流程的标识。这必须基于输入消息的一个数据项。而<STRONG>工作流</STRONG>系统在每个流程实例生成同时生成了实例ID，客户端在后续调用引擎API时使用这个ID。 </SPAN>
<LI class=text2><U><STRONG>工作流引擎</STRONG>API与抽象服务端点（endpoint）</U>：<STRONG>工作流</STRONG>系统提供一组集中的API，客户端通过调用API完成与所有流程实例的交互。在可执行业务流程中，每个流程表现为一个服务。这意味着对于每个流程定义都有一个不同的访问点。 </LI></UL>
<H2>学术界</H2><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;学术界对<STRONG>工作流</STRONG>的研究可以回溯到上个世纪七十年代。在当前，研究领域趋向于认为<A href="http://www.daimi.au.dk/PetriNets/">petr 网</A>是<A href="http://tmitwww.tm.tue.nl/research/patterns/main_conclusions.htm">所有流程定义语言之母</A>。关于petri网已有大量先进的分析技术，去年在 <A href="http://tmitwww.tm.tue.nl/bpm2003/">2003 conference on Business Process Management</A>上我有幸会晤了Petri教授。对于大部分人能够访问和理解的有关Petyri网最好的研究之一是<STRONG>工作流</STRONG>模式<A href="http://tmitwww.tm.tue.nl/research/patterns">(workflow patterns)</A>。<STRONG>工作流</STRONG>模式比较了大量的<STRONG>工作流管理系统</STRONG>并以petri网的术语表述了通用流程建模概念。</SPAN> 
<H2 id=opensourceprojects>开放源代码项目</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;最后我们看看真实世界中的<STRONG>工作流管理系统</STRONG>。选择一个<STRONG>工作流管理系统</STRONG>是一件困难的事情，但有选择总比没有选择好。:-) 本文阐述<STRONG>工作流</STRONG>基本概念的目的之一，就是使你能够作更好的选择。但我也意识到，对于现在的软件架构师来说，选择<STRONG>工作流</STRONG>系统是一件最具挑战性的工作。</P>
<P><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;下面的列表来源于三个地方：<A href="http://jbpm.org/article.html">my previous article</A>, <A href="http://www.manageability.org/blog/stuff/workflow_in_java/view">the list of Carlos E Perez</A>, 和 <A href="http://www.topicus.nl/topwfm/Tooloverzicht.htm">list by Topicus</A>. </SPAN></P>
<UL>
<LI><SPAN class=text2><B><A href="http://www.jbpm.org/">jBpm</A></B> - jBpm是本文作者编写的一个灵活可扩展的<STRONG>工作流管理系统</STRONG>。作为jBpm运行时server输入的业务流程使用简单强大的语言表达并打包在流程档案中。jBmp将<STRONG>工作流</STRONG>应用开发的便利性和杰出的企业应用集成（EAI）能力结合了起来。jBmp包括一个Web应用程序和一个日程安排程序。jBmp是一组J2SE组件，可以作为J2EE应用集群部署。 </SPAN>
<LI><SPAN class=text2><B><A href="http://openebxml.sourceforge.net/">OpenEbXML</A></B> - OpenebXML项目致力于提供一个ebXML框架，主要支持不久将由 UN/CEFACT和OASIS发布的ebXML规范2.0版。 </SPAN>
<LI><SPAN class=text2><B><A href="http://werkflow.codehaus.org/">Werkflow</A></B> - Werkflow是一个灵活可扩展的基于流程和状态的<STRONG>工作流引擎</STRONG>。它的目标是满足可以想象的所有工作流程，从企业级的业务流程到小范围的用户交互流程。通过使用可插拔和分层结构，可以方便地容纳各种<STRONG>工作流</STRONG>语义。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.opensymphony.com/osworkflow">OSWorkflow</A></B> - OSWorkflow最独到之处是绝对的灵活。 </SPAN>
<LI><SPAN class=text2><B><A href="http://wfmopen.sourceforge.net/">wfmOpen</A></B> - WfMOpen是WfMC和OMG中所谓<STRONG>工作流</STRONG>设施（workflow facility） (<STRONG>工作流引擎</STRONG>)的J2EE实现。<STRONG>工作流</STRONG>通过扩展的XPDL描述。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.ofbiz.org/docs/workflow.html">OFBiz</A></B> - OFBiz<STRONG>工作流引擎</STRONG>基于WfMC和OMG的规范，使用XPDL作为流程定义语言。 </SPAN>
<LI><SPAN class=text2><B><A href="http://bonita.forge.objectweb.org/">ObjectWeb Bonita</A></B> - Bonita是一个符合WfMC规范、灵活的协同<STRONG>工作流</STRONG>系统。 对于各种动作如流程概念建模、定义、实例化、流程控制和用户交互等提供了全面的集成图形工具。 100% 基于浏览器、使用SOAP和XML数据绑定技术的Web Services封装了已有的<STRONG>工作流</STRONG>业务方法并将它们以基于J2EE的Web Service形式发布。基于活动预测模型的第三代<STRONG>工作流引擎</STRONG>。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.bigbross.com/bossa/">Bigbross Bossa</A></B> -速度非常快、轻量级的引擎，使用富有表达能力的Petri网定义<STRONG>工作流</STRONG>，不要求关系数据库，使用简单，能和Java应用集成。事实上，它是按嵌入式设计的。 </SPAN>
<LI><SPAN class=text2><B><A href="http://xflow.sourceforge.net/">XFlow</A></B> - XFlow运行于EJB和servlet容器中。 </SPAN>
<LI><SPAN class=text2><B><A href="http://taverna.sourceforge.net/">Taverna</A></B> - Taverna项目的目标是提供一种语言和软件工具，方便在eScience中使用<STRONG>工作流</STRONG>和分布计算技术。 </SPAN>
<LI><SPAN class=text2><B><A href="http://shark.enhydra.org/">Enhydra Shark</A></B> - Shark完全基于WfMC和OMG标准，使用 XPDL作为<STRONG>工作流</STRONG>定义语言。流程和活动的存储使用Enhydra DODS。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.powerfolder.org/">PowerFolder</A></B> - PowerFolder包括开发人员使用的studio，管理环境和一个运行时引擎。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.dstc.edu.au/Research/Projects/Pegamento/Breeze/breeze.html">Breeze</A></B> - Breeze一个轻量级、跨平台、基于组件的<STRONG>工作流引擎</STRONG>原型。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.openbusinessengine.org/index.html">Open Business Engine</A></B> - Open Business Engine是一个开放源码的Java<STRONG>工作流引擎</STRONG>，支持WfMC规范，包括接口1（XPDL）、接口2/3（WAPI）和接口5。OBE为活动的运行提供了一个可控的集中环境。OBE主要基于J2EE实现。 </SPAN>
<LI><SPAN class=text2><B><A href="http://openwfe.sourceforge.net/">OpenWFE</A></B> - OpenWFE是一个开放源码的Java<STRONG>工作流引擎</STRONG>。 它包括可升级的三个组件：引擎、工作列表和Web界面。它的流程定义语言虽然使用XML格式，其灵感来源于 Scheme，一种Lisp方言。 </SPAN>
<LI><SPAN class=text2><B><A href="http://freefluo.sourceforge.net/">Freefluo</A></B> - Freefluo是一个使用Web Service的<STRONG>工作流</STRONG>协同工具，可以处理WSDL的Web Service调用。支持两种XML格式的<STRONG>工作流</STRONG>语言：IBM的WSFL和XScufl。Freefluo非常灵活，它的核心是不与任何<STRONG>工作流</STRONG>语言或执行架构关联的可重用协同框架。 Freefluo包括可执行使用WSFL一个子集描述的<STRONG>工作流</STRONG>的运行库。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.tripodsoft.com/products/intro.htm">ZBuilder</A></B> - ZBuilder3是第二代<STRONG>工作流</STRONG>开发管理系统，也是一个开放源码产品。它为不同的<STRONG>工作流引擎</STRONG>和<STRONG>工作流</STRONG>定义了一组标准的JMX管理接口。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.smartcomps.org/twister/">Twister</A></B> - Twister的目标是提供新一代、易集成、应用Java领域中最新成果、面向B2B的<STRONG>工作流</STRONG>解决方案。流程引擎基于BPEL业务流程规范和Web Service标准。 </SPAN>
<LI class=text2><B><A href="http://con-cern.org/">Con:cern</A></B> - con:cern<STRONG>工作流引擎</STRONG>基于扩展的案例（case）处理方法，流程由一组具有前后条件的活动组成。 </LI></UL>
<H2 id=commercialvendors>商业软件提供商</H2>
<UL>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://e-docs.bea.com/wlintegration/v2_1/interm/bpmhome.htm">Bea's WLI</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.carnot-usa.com/">Carnot</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.dralasoft.com/products/workflow/">Dralasoft</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.filenet.com/">Filenet</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.fsw.fujitsu.com/products/InterstageSuite/BPM/overview.html">Fujitsu's i-Flow</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www-106.ibm.com/developerworks/ibm/library/i-holo/">IBM's holosofx tool</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.intalio.com/products/index.html">Intalio</A> </SPAN>
<LI class=text2><A class=style4 href="http://www.joinwork.net/" target=_blank><STRONG>Joinwork</STRONG></A> （译者加:-) ） 
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.lombardisoftware.com/">Lombardi</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://oakgrovesystems.com/">Oakgrove's reactor</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.oracle.com/ip/deploy/ias/integration/">Oracle's integration platform</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.qlinktech.com/">Q-Link</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.sap.com/solutions/netweaver/">SAP's NetWeaver</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.savvion.com/products/">Savvion</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.seebeyond.com/products/ICAN_productsEinsight.asp">Seebeyond</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.sonicsoftware.com/products/sonic_orchestration_server/">Sonic's orchestration server</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.staffware.com/products/">Staffware</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.ultimus.com/">Ultimus</A> </SPAN>
<LI style="PADDING-BOTTOM: 0px"><SPAN class=text2><A href="http://www.versata.com/products/inSuite/products.html">Versata</A> </SPAN>
<LI class=text2 style="PADDING-BOTTOM: 0px"><A href="http://www.webmethods.com/content/1,1107,Model,FF.html">WebMethod's process modeling</A> </LI></UL>
<H2>工具目录</H2>
<UL>
<LI><SPAN class=text2><A href="http://dmoz.org/Computers/Software/Workflow/Products/">http://dmoz.org/Computers/Software/Workflow/Products/</A> </SPAN>
<LI class=text2><A href="http://is.twi.tudelft.nl/~hommes/tools.html">A collection of links to tools for modelling business processes and workflows maintained by Bart-Jan Hommes at TU Delft, the Netherlands.</A> </LI></UL>
<H2>规范</H2>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;Michael zur Muehlen作了一个所有<STRONG>工作流</STRONG>相关规范的介绍性的<A href="http://www.wfmc.org/standards/docs/Process_Management_Standards_files/frame.htm" target=_blank>幻灯片</A>，很不错。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;我同意<A href="http://www.staffware.com/understanding-bpm/useful-reading/The-Problem-with-Process-Management-Standards.asp?ComponentID=8058&amp;SourcePageID=7348#1">John Pyke</A> 和 <A href="http://tmitwww.tm.tue.nl/research/patterns/download/ieeewebflow.pdf">Wil van der Aalst</A> 的观点：<STRONG>工作流</STRONG>标准还处于制定阶段。现在存在大量相互丛叠的规范。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;在我看来，导致规范如此之多而同时每个规范的应用又很有限的原因是，在<STRONG>工作流</STRONG>最基础概念上大家达成的共识很少。<STRONG>工作流</STRONG>是最容易让你感到心烦的话题，因为<STRONG>工作流</STRONG>本身的概念会和其他相关概念和技术混淆在一起。可以举一个具体的例子，比如说<STRONG>工作流</STRONG>完全是对Web Service的补充。你可以通过暴露接口以Web Service的方式访问一个<STRONG>工作流</STRONG>管理系统，但是不能假定总是必须通过Web Service接口访问<STRONG>工作流</STRONG>系统接口。一些规范造成了这样的假设。除了Web Service，其他容易混淆的概念和技术包括：Email、流程之间的通讯、Web应用和组织结构。</P>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;在<STRONG>工作流</STRONG>领域第一个致力于标准化工作的是<A href="http://wfmc.org/">Workflow Management Coalition</A> (WfMC)，开始于 1993。 WfMC发布的<A href="http://wfmc.org/standards/model.htm">参考模型</A>很不错，它定义了<STRONG>工作流管理系统</STRONG>和其他相关部分之间的接口。WfMC的另一项成果是<A href="http://www.wfmc.org/standards/docs/TC-1025_10_xpdl_102502.pdf">XPDL</A>规范。 XPDL定义了描述<STRONG>工作流</STRONG>声明部分（declarative part）的XML结构。我个人认为，参考模型和XPDL是目前最好的规范。</P>
<UL>
<LI><SPAN class=text2><A href="http://www.jcp.org/en/jsr/detail?id=207"><B>JSR 207: Java的流程定义</B></A> -是由<A href="http://www.jcp.org/">Java Community Process (JCP)</A> 发起，如何在J2EE应用服务器中实现业务流程自动化的标准。其基本模型是定义一个特殊类型的ejb session bean，作为一个业务流程的接口。JSR207标准化一组XML元标记（meta tags）作为JSR175元数据的一部分。JSR207 将session bean和元数据作为ejb容器的输入，然后生成绑定方法的代码，这些方法在元数据中描述。此规范还处于初级阶段，没有发布任何内容。专家小组成立于 March 2003. </SPAN>
<LI><SPAN class=text2><B><A href="http://www.wfmc.org/">WfMC's</A> <A href="http://www.wfmc.org/standards/docs/TC-1025_10_xpdl_102502.pdf">XPDL</A></B> - WfMC是由约300家成员参加的组织，基于参考模型定义了一系列的标准。参考模型用用例（use case）的形式描述了<STRONG>工作流</STRONG>系统和其他相关部分之间的关系。XPDL是WfMC制定的描述业务流程控制流（control flow ）的XML格式规范。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.ebxml.org/">ebXML's</A> <A href="http://ebxml.org/specs/ebBPSS.pdf">BPSS</A></B> - ebXML是协同流程的相关标准集，主要关注不同公司流程之间的通讯。可以看作EDI的继承者。 ebXML是由OASIS和UN/CEFACT联合发起。 BPSS 是ebXML的规范，其中的概念和本文阐述的很接近。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.bpmi.org/">BPMI's</A> BPML &amp; WSCI</B> - (Intalio, Sun, SAP, ...)BPMI 也定义了一个规范 (BPMN) ，描述如何将“可执行”业务流程可视化的表现。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www-106.ibm.com/developerworks/library/ws-bpel/">BPEL</A></B> - (Microsoft, BEA, IBM, SAP &amp; Siebel) BPEL由一系列基于消息交换的规范（ XLANG, WSFL, BPML）产生。还有一个将此规范引入到Java的提案: <A href="http://www-106.ibm.com/developerworks/webservices/library/ws-bpelj/">BPELJ</A>。 此规范描述如何处理输入的消息，而不是对流程状态进行建模。就像本文提到的，它不是一个关于业务流程规格化定义的规范。简单的说，可以将它看作XML形式的编程语言，提供将WSDL-Services组合成控制流的能力。顾名思义，此规范重点在（也不只限于）Web Service。</SPAN> 
<LI class=text2><B><A href="http://www.omg.org/docs/formal/00-05-02.pdf">OMG's Workflow management facility</A></B> - 基于WfMC规范，定义如何向CORBA转换。 
<LI><SPAN class=text2><B><A href="http://www.uml.org/">UML</A></B> - UML定义了建模和设计软件系统的9类图。每类图包括可视化的表示和语义。其中活动图的目的就是要可视化的表现业务流程。 注意到在一个流程定义包含四个层次的内容，我想指出的是：一个流程定义包含的内容远远多于它的可视化部分。UML只涉及了可视化部分。 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.rosettanet.org/">RosettaNet</A></B> - RosettaNet主要定义了一组 Partner Interface Processes (PIP). 一个 PIP 描述了一个有两个交易参与者、包括消息格式的流程。 </SPAN>
<LI class=text2><B><A href="http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=ubl">UBL</A></B> - The Universal Business Language (UBL)定义了用于不同组织间通讯的XML文档标准库。可以看作是对ebXML的补充，因为ebXML只定义了建立组织间流程的基础。此规范的竞争对手是 RosettaNet标准中的一个子集。 </LI></UL><!-- =================================================================================== -->
<H1>结论</H1>
<P><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;我在本文中指出<STRONG>工作流</STRONG>市场还属于年轻而又混乱（young and wild）的阶段，但已经有可靠的工具存在了: </SPAN>
<OL>
<LI><SPAN class=text2>到目前，像J2EE和.NET这样成熟的集成平台才可用。在这样一个平台上运行</SPAN><SPAN class=style5><STRONG><FONT face=宋体 size=2>工作流</FONT></STRONG></SPAN><SPAN class=text2><STRONG>管理系统</STRONG>才能真正发挥<STRONG>工作流</STRONG>系统的附加价值。这也是为什么只有在今天，<STRONG>工作流</STRONG>系统才被重新发现。 </SPAN>
<LI class=text2>在 'The case for workflow'一节，我们介绍了引入<STRONG>工作流管理系统</STRONG>，是如何同时在技术和业务上带来投资回报的。 
<LI><SPAN class=text2><STRONG>工作流</STRONG>在技术发展曲线的位置表明，BPM和<STRONG>工作流</STRONG>中使用的概念还需要明确。 </SPAN>
<LI class=text2>“开放源代码项目”和“商业软件提供商”列表中的工具，可以让你获得<STRONG>工作流</STRONG>和<STRONG>业务流程管理</STRONG>的益处。 </LI></OL>
<P><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;从以上所有这些中能得到的结论是：</SPAN> 
<OL>
<LI><SPAN class=text2>规范还没有成熟，没有标准被大范围采用 </SPAN>
<LI><SPAN class=text2>对于现在想应用BPM的公司来讲，比较<STRONG>工作流</STRONG>系统是一个极其困难的挑战 </SPAN>
<LI class=text2>尽管标准化工作慢了一拍，可好的<STRONG>工作流管理系统</STRONG>还是有的。这对于已经在挑选<STRONG>工作流</STRONG>系统的组织来说是一个好消息。 </LI></OL>
<P class=text2>&nbsp;&nbsp;&nbsp;&nbsp;我希望本文能够激发你对<STRONG>工作流</STRONG>的兴趣并且能够为你进行有效的对比提供正确的背景知识。</P><!-- =================================================================================== -->
<H1 id=furtherreading>Further reading</H1>
<UL>
<LI><SPAN class=text2><B><A href="http://tmitwww.tm.tue.nl/research/patterns/">Workflow Patterns</A></B> -Wil van der Aalst教授的<STRONG>工作流</STRONG>模式学术研究网站 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.ebpml.org/">ebpml.org</A></B> - 有关业务流程管理和<STRONG>工作流</STRONG>的网站，信息量大、全面 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.bpmg.org/">Business process management group</A></B> - The Business Process Management Group (founded in 1992) is a global business club exchanging ideas and best practice in business process and change management. </SPAN>
<LI><SPAN class=text2><B><A href="http://www.enix.co.uk/">Enix</A></B> - 英国流程管理顾问公司，在网站上有不错的内容 </SPAN>
<LI><SPAN class=text2><B><A href="http://www.ebizq.net/">ebizq.net</A></B> - Commercial community that has booths, webinars and other interesting stuff put together in a nice website. </SPAN>
<LI><SPAN class=text2><B><A href="http://viking.gmu.edu/http/syst511/vg511/AppC.html">An introduction to petri nets</A></B> - The title says it all. </SPAN>
<LI><SPAN class=text2><B><A href="http://www.workflow-research.de/Publications/PDF/MIZU-ITM(2004).pdf">Organizational Management in Workflow Applications</A>- An interesting article that discusses the relation between business processes and the organisational data involved. </SPAN>
<LI><SPAN class=text2><B><A href="http://devresource.hp.com/drc/technical_white_papers/WSOrch/WSOrchestration.pdf">Web services orchestration</A></B> - an HP-paper that reviews the emerging technologies tools and standards. (Januari 2003) </SPAN>
<LI class=text2><B><A href="http://www.webservicesarchitect.com/content/articles/oriordan01.asp">Business process standards for web services</A></B> - An article that discusses the various aspects involved in workflow and BPM. </LI></UL>
<H1>Thanks</H1><SPAN class=text2>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>A special thanks for Gustavo Maciel Dias Vieira and Jef Vanbockryck for their valuable feedback on the draft versions of this article. </B><img src ="http://www.blogjava.net/qq13367612/aggbug/16024.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-13 16:47 <a href="http://www.blogjava.net/qq13367612/articles/16024.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高速缓存和连接池对访问数据库性能的影响</title><link>http://www.blogjava.net/qq13367612/articles/16131.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 19 Sep 2005 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16131.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16131.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16131.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16131.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16131.html</trackback:ping><description><![CDATA[<P>初识高速缓存和连接池 </P>
<P>设想这样一种情形：你突然口渴，需要一杯水来缓解，从心情上来讲，当然是越快越好了。通常，一杯水的产生包括从水源（井水、河水或江水、甚至海水等）抽取，通过管道传输和设备净化，才到达你饮水的容器中。上述过程是必须的，但并不是每一杯水的产生都必须把上述过程重复一次。你可以用一个大一点的容器（例如缸或罐等）来盛大量的水，喝水之前分到杯子小部分中即可，你的代价只是把水从缸转移到杯子；你还可以在大量用水（例如洗澡或洗衣服等）时，只需打开水阀，而不必临时铺设通往水源的管道和购买净化水的设备。因为水是人们生活不可缺少的东西，每时每刻都在被大量地使用，而且物理本质也完全相同，所以政府会铺设管道和建设水处理站，完成那些比较困难的工作，达到资源共享的目的，而你也可针对自己的需求，用容器来盛那些具有特定用途的水。本文将要和你讨论的高速缓存和连接池与上述特定容器和传输管道有很多相似之处，它们都达到了同一个目的：在满足用户意愿的前提下，尽可能地共享资源，以提高整个系统的性能。 </P>
<P>高速缓存和连接池是数据访问中的重要技术，某些情况下的应用对访问数据库的性能有巨大的提高，而且都得到了数据库业界的普遍支持。前者由DBMS厂商针对自己的数据库实现，提供可供用户配置的方案；后者是JDBC的一个标准接口，由支持J2EE技术的应用服务器厂商提供具体的实现，而你的Java程序代码无需更改。本文将向你简单介绍高速缓存和连接池的概念和机制，并以PointBase数据库为例向你展示高速缓存的应用，而一个简单的连接池应用场景将向你描述应用的条件和提高的性能。 </P>
<P>Cache（高速缓存）和Connection Pool（连接池）的概念和机制 </P>
<P>它们不是数据库独有的技术，但却得到数据库业界的普遍支持，并在其它数据存取和对象复用领域有很多类似的应用。 </P>
<P>Cache（高速缓存） <BR>作为个人计算机的日常使用者，你肯定听说过这些名词：Cache（高速缓存）、Memory（内存）、Hard disk（硬盘）。它们都是数据存取单元，但存取速度却有很大差异，呈依次递减的顺序。对于CPU来说，它可以从距离自己最近的Cache高速地存取数据，而不是从内存和硬盘以低几个数量级的速度来存取数据。而Cache中所存储的数据，往往是CPU要反复存取的数据，有特定的机制（或程序）来保证Cache内数据的命中率（Hit Rate）。因此，CPU存取数据的速度在应用高速缓存后得到了巨大的提高。 </P>
<P>对于数据库来说，厂商的做法往往是在内存中开辟相应的区域来存储可能被多次存取的数据和可能被多次执行的语句，以使这些数据在下次被访问时不必再次提交对DBMS的请求和那些语句在下次执行时不必再次编译。 </P>
<P><BR>因为将数据写入高速缓存的任务由Cache Manager负责，所以对用户来说高速缓存的内容肯定是只读的。需要你做的工作很少，程序中的SQL语句和直接访问DBMS时没有分别，返回的结果也看不出有什么差别。而数据库厂商往往会在DB Server的配置文件中提供与Cache相关的参数，通过修改它们，可针对我们的应用优化Cache的管理。下图是在Win2K中配置MS Access数据源的界面，在"驱动程序"部分你可设置的页超时和缓冲区大小就是和Cache有关的参数。在后面的讨论中，我将展示一个更复杂的数据库，向你解释Cache对访问数据库性能的影响和如何寻找最优的配置方案。 </P>
<P><BR>Connection Pool（连接池） </P>
<P>池是一个很普遍的概念，和缓冲存储有机制相近的地方，都是缩减了访问的环节，但它更注重于资源的共享。下图展示了建立"调制解调器池"以共享调制解调器资源的VPN拨号方案： </P>
<P><BR>对于访问数据库来说，建立连接的代价比较昂贵，因此，我们有必要建立"连接池"以提高访问的性能。我们可以把连接当作对象或者设备，池中又有许多已经建立的连接，访问本来需要与数据库的连接的地方，都改为和池相连，池临时分配连接供访问使用，结果返回后，访问将连接交还。 </P>
<P>JDBC 1.0标准及其扩展中没有定义连接池，而在JDBC 2.0标准的扩展中定义了与连接池相关的接口。与接口对应的类由应用服务器厂商实现，你可在对服务器的管理过程中调节某个数据库连接池的参数。下图简略地描述了连接池的运行机制： </P>
<P><BR>高速缓存的参数设定 </P>
<P>在PointBase数据库DB Server的配置参数列表中，我们可以找到这几个参数：cache.checkpointinterval、cache.size、SQLCaching.size等。下表是对各个参数的描述： </P>
<P>参数名 参数描述 <BR>cache.checkpointinterval 检查点的时间间隔 <BR>cache.size 高速缓存的最大页数（素数时，性能最好） <BR>SQLCaching.size 高速缓存中SQL语句的个数 </P>
<P><BR>对于cache.checkpointinterval来说，和前面Access界面中的页超时是一个概念，它指定了页面内容更新的时间间隔，这取决于你的应用对时效性的要求程度。 </P>
<P>对于cache.size来说，指定了页面的个数，一般应设定为符合你查询结果的需求。至于为何是素数，我也纳闷，不过还是遵照厂商的指示吧。 </P>
<P>对于SQLCaching.size来说，指定了存储的经过编译的SQL语句的个数，你可以把它设定为0，从而取消这个选项。 </P>
<P>使用Cache后，性能到底有多大提高？我打开PointBase的Console通过JDBC驱动访问Server，将SQL菜单下的Timing Mode选上，以显示各个步骤的耗费时间。执行语句是： </P>
<P>SELECT * FROM PRODUCT_TBL </P>
<P> </P>
<P> </P>
<P><BR>第一次访问，总计耗时1082毫秒，而编译耗时771毫秒。 </P>
<P>紧接着的第二次访问，总计耗时仅为160毫秒，而编译耗时为0。 </P>
<P>再接着的第三次访问，总计耗时仅为91毫秒，编译耗时也为0。 </P>
<P>关闭Console，等待超过30秒之后，重新开启Console，执行相同的语句，总计耗时210毫秒，编译耗时为20毫秒。 </P>
<P>自等待超过30秒之后，执行语句，总计耗时101毫秒，编译耗时为0。 </P>
<P>由此可以看出，高速缓存的应用大大提高了访问数据库的性能，而其参数的设定则要依据前面对它们的描述来进行，需要你仔细阅读数据库的配置文档。 </P>
<P>连接池的设置和应用 </P>
<P>我选择IBM公司的应用服务器平台WebSphere来给大家演示连接池的设置，使你面对友好的Web界面，可以体验到非常简易的操作场景。 </P>
<P>首先，我们进入WebSphere的管理控制台，这是一个非常漂亮的Web界面： </P>
<P><BR>紧接着，我选定一个数据源：Session Persistence datasource，就可看到这个数据源的属性配置了。在这儿，仅仅列举和连接池有关的属性： </P>
<P>Minimum Pool Size 池中保持的连接的最小数目；有新的请求，且没有激活连接可供使用时，池中连接数将增大，到最大连接数为止 <BR>Maximum Pool Size 池中保持的连接的最大数目；当这个数目达到，且没有激活连接可供使用时，新的请求将等待 <BR>Connection Timeout 当连接数达到最大值，且激活连接都在被使用时，新的请求等待的时间 <BR>Idle Timeout 连接可在池中闲置的时间；超过将释放资源，到最小连接数为止 <BR>Orphan Timeout 连接在被应用控制时，可闲置的时间；超过将返回池中 </P>
<P><BR>你可以根据需要来修改这些数值，以满足你的应用需要。接下来，我们讨论一下连接池的应用。 </P>
<P>EJB访问数据库（场景1，使用JDBC 1.0） import java.sql.*; <BR>import javax.sql.*; <BR>... <BR>public class AccountBean implements EntityBean { <BR>... <BR>public Collection ejbFindByLastName(String lName) { <BR>try { <BR>String dbdriver = new initialContext().lookup("java:comp/env/DBDRIVER").toString(); <BR>Class.forName(dbdriver).newInstance(); <BR>Connection conn = null; <BR>conn = DriverManager.getConnection("java:comp/env/DBURL", "userID", "password"); <BR>... <BR>conn.close(); <BR>} <BR>... <BR>} </P>
<P> </P>
<P> </P>
<P><BR>如果EntityBean是一个共享组件，那么每次客户请求时，都要建立和释放与数据库的连接，这成为影响性能的主要问题。 </P>
<P>EJB访问数据库（场景2，使用JDBC 2.0） import java.sql.*; <BR>import javax.sql.*; <BR>// import here vendor specific JDBC drivers </P>
<P>public ProductPK ejbCreate() { <BR>try { <BR>// initialize JNDI lookup parameters <BR>Context ctx = new InitialContext(parms); <BR>... <BR>ConnectionPoolDataSource cpds = (ConnectionPoolDataSource)ctx.lookup(cpsource); <BR>... <BR>// Following parms could all come from a JNDI look-up <BR>cpds.setDatabaseName("PTDB"); <BR>cpds.setUserIF("XYZ"); <BR>... <BR>PooledConnection pc = cpds.getPooledConnection(); <BR>Connection conn = pc.getConnection(); <BR>... <BR>// do business logic <BR>conn.close(); <BR>} <BR>... <BR>} <BR>EJB组件利用JNDI的lookup()方法定位数据库的连接池资源，利用getConnection()方法得到已经打开的数据库连接，而用close()来释放连接，放回池中。因此，与场景1相比，少了与数据库建立物理连接的损耗。对于原本要频繁打开和关闭物理连接的应用来说，通过这种建立逻辑连接并复用的方法，性能肯定能够得到大幅度提高。 </P>
<P>性能问题的深远思索 </P>
<P>性能问题并不局限于数据库的应用上，而是存在于每个软件系统中。我们希望软件系统付出的最小，而获得的最大，因而无时无刻不在优化它们。通过《Java程序访问数据库的速度瓶颈问题的分析和解决》和本文，我对Java程序访问数据库的性能问题做了分析，并提供了优化Java程序的解决方案，希望对你有所帮助。 </P>
<P>也许你会关心碰到类似的性能问题时应如何分析和解决，我就针对此次探讨"访问数据库的速度瓶颈"问题的过程中碰到的难题、网友的意见和自己的体会，作一个关于"方法论"的经验总结，希望能够抛砖引玉，更好地解决类似的问题。 </P>
<P>解决性能问题的几条经验 </P>
<P>不要让硬件的低配置成为软件正常运行的障碍，后者有升级前者的需求，请立即满足；经常碰到这样的问题"P166+64M的机子跑Win2K+MySql+JBoss，能跑么？"我在回答"可以"的同时只有对着屏幕发呆了。 <BR>尽量使用商业软件，并享受良好的售后技术支持；如果你没有黑客精神，请不要使用自由软件。 <BR>分析好自己的问题，也许它的本质和他人的不同；没有一把钥匙可打开的任一把锁。 <BR>确定瓶颈环节的位置；解决了瓶颈问题，往往整个的性能问题就解决了，千万不要抓着边缘的问题不放。 <BR>将瓶颈环节细分为多个顺序的流程，用逐个替代的方法来试探瓶颈的核心位置；细分问题使你都问题有更进一步的了解。 <BR>做自己能做的和该做的事情，始终面向自己的现实问题；不要尝试那些应该由厂商解决的问题（例如，自己写个JDBC驱动）。 <BR>他人的方案只供自己参考，解决要靠自己思考；你我应用的情形不同，应用解决方案要在理解他人的方案之后。 <BR>观察新技术，应用新技术；它往往包含了前人对问题解决的思路，只是对你来说不可见。 <BR>问题得到解决后，立即罢手，并汇报结果；在现实问题得到解决后，没有必要花费精力在非核心的问题上，也许它们永远不会被碰到，不要假想问题让自己解决。 <BR>上述经验为个人即兴的总结，并未有严谨的逻辑推导，仅代表我解决技术问题的思路，供你碰到类似问题时参考。 </P>
<P>可以提前告诉大家的是，下一篇文章我将把主题转移到JDO的介绍和讨论上。这个陌生的API已经进入Final Draft了，可它到底能够给数据存取带来什么好处，和JDBC有什么关联的地方，《JDO（Java Data Object）的发展和介绍》将向你娓娓道来。 </P><![CDATA[<P>初识高速缓存和连接池 </P>
<P>设想这样一种情形：你突然口渴，需要一杯水来缓解，从心情上来讲，当然是越快越好了。通常，一杯水的产生包括从水源（井水、河水或江水、甚至海水等）抽取，通过管道传输和设备净化，才到达你饮水的容器中。上述过程是必须的，但并不是每一杯水的产生都必须把上述过程重复一次。你可以用一个大一点的容器（例如缸或罐等）来盛大量的水，喝水之前分到杯子小部分中即可，你的代价只是把水从缸转移到杯子；你还可以在大量用水（例如洗澡或洗衣服等）时，只需打开水阀，而不必临时铺设通往水源的管道和购买净化水的设备。因为水是人们生活不可缺少的东西，每时每刻都在被大量地使用，而且物理本质也完全相同，所以政府会铺设管道和建设水处理站，完成那些比较困难的工作，达到资源共享的目的，而你也可针对自己的需求，用容器来盛那些具有特定用途的水。本文将要和你讨论的高速缓存和连接池与上述特定容器和传输管道有很多相似之处，它们都达到了同一个目的：在满足用户意愿的前提下，尽可能地共享资源，以提高整个系统的性能。 </P>
<P>高速缓存和连接池是数据访问中的重要技术，某些情况下的应用对访问数据库的性能有巨大的提高，而且都得到了数据库业界的普遍支持。前者由DBMS厂商针对自己的数据库实现，提供可供用户配置的方案；后者是JDBC的一个标准接口，由支持J2EE技术的应用服务器厂商提供具体的实现，而你的Java程序代码无需更改。本文将向你简单介绍高速缓存和连接池的概念和机制，并以PointBase数据库为例向你展示高速缓存的应用，而一个简单的连接池应用场景将向你描述应用的条件和提高的性能。 </P>
<P>Cache（高速缓存）和Connection Pool（连接池）的概念和机制 </P>
<P>它们不是数据库独有的技术，但却得到数据库业界的普遍支持，并在其它数据存取和对象复用领域有很多类似的应用。 </P>
<P>Cache（高速缓存） <BR>作为个人计算机的日常使用者，你肯定听说过这些名词：Cache（高速缓存）、Memory（内存）、Hard disk（硬盘）。它们都是数据存取单元，但存取速度却有很大差异，呈依次递减的顺序。对于CPU来说，它可以从距离自己最近的Cache高速地存取数据，而不是从内存和硬盘以低几个数量级的速度来存取数据。而Cache中所存储的数据，往往是CPU要反复存取的数据，有特定的机制（或程序）来保证Cache内数据的命中率（Hit Rate）。因此，CPU存取数据的速度在应用高速缓存后得到了巨大的提高。 </P>
<P>对于数据库来说，厂商的做法往往是在内存中开辟相应的区域来存储可能被多次存取的数据和可能被多次执行的语句，以使这些数据在下次被访问时不必再次提交对DBMS的请求和那些语句在下次执行时不必再次编译。 </P>
<P><BR>因为将数据写入高速缓存的任务由Cache Manager负责，所以对用户来说高速缓存的内容肯定是只读的。需要你做的工作很少，程序中的SQL语句和直接访问DBMS时没有分别，返回的结果也看不出有什么差别。而数据库厂商往往会在DB Server的配置文件中提供与Cache相关的参数，通过修改它们，可针对我们的应用优化Cache的管理。下图是在Win2K中配置MS Access数据源的界面，在"驱动程序"部分你可设置的页超时和缓冲区大小就是和Cache有关的参数。在后面的讨论中，我将展示一个更复杂的数据库，向你解释Cache对访问数据库性能的影响和如何寻找最优的配置方案。 </P>
<P><BR>Connection Pool（连接池） </P>
<P>池是一个很普遍的概念，和缓冲存储有机制相近的地方，都是缩减了访问的环节，但它更注重于资源的共享。下图展示了建立"调制解调器池"以共享调制解调器资源的VPN拨号方案： </P>
<P><BR>对于访问数据库来说，建立连接的代价比较昂贵，因此，我们有必要建立"连接池"以提高访问的性能。我们可以把连接当作对象或者设备，池中又有许多已经建立的连接，访问本来需要与数据库的连接的地方，都改为和池相连，池临时分配连接供访问使用，结果返回后，访问将连接交还。 </P>
<P>JDBC 1.0标准及其扩展中没有定义连接池，而在JDBC 2.0标准的扩展中定义了与连接池相关的接口。与接口对应的类由应用服务器厂商实现，你可在对服务器的管理过程中调节某个数据库连接池的参数。下图简略地描述了连接池的运行机制： </P>
<P><BR>高速缓存的参数设定 </P>
<P>在PointBase数据库DB Server的配置参数列表中，我们可以找到这几个参数：cache.checkpointinterval、cache.size、SQLCaching.size等。下表是对各个参数的描述： </P>
<P>参数名 参数描述 <BR>cache.checkpointinterval 检查点的时间间隔 <BR>cache.size 高速缓存的最大页数（素数时，性能最好） <BR>SQLCaching.size 高速缓存中SQL语句的个数 </P>
<P><BR>对于cache.checkpointinterval来说，和前面Access界面中的页超时是一个概念，它指定了页面内容更新的时间间隔，这取决于你的应用对时效性的要求程度。 </P>
<P>对于cache.size来说，指定了页面的个数，一般应设定为符合你查询结果的需求。至于为何是素数，我也纳闷，不过还是遵照厂商的指示吧。 </P>
<P>对于SQLCaching.size来说，指定了存储的经过编译的SQL语句的个数，你可以把它设定为0，从而取消这个选项。 </P>
<P>使用Cache后，性能到底有多大提高？我打开PointBase的Console通过JDBC驱动访问Server，将SQL菜单下的Timing Mode选上，以显示各个步骤的耗费时间。执行语句是： </P>
<P>SELECT * FROM PRODUCT_TBL </P>
<P> </P>
<P> </P>
<P><BR>第一次访问，总计耗时1082毫秒，而编译耗时771毫秒。 </P>
<P>紧接着的第二次访问，总计耗时仅为160毫秒，而编译耗时为0。 </P>
<P>再接着的第三次访问，总计耗时仅为91毫秒，编译耗时也为0。 </P>
<P>关闭Console，等待超过30秒之后，重新开启Console，执行相同的语句，总计耗时210毫秒，编译耗时为20毫秒。 </P>
<P>自等待超过30秒之后，执行语句，总计耗时101毫秒，编译耗时为0。 </P>
<P>由此可以看出，高速缓存的应用大大提高了访问数据库的性能，而其参数的设定则要依据前面对它们的描述来进行，需要你仔细阅读数据库的配置文档。 </P>
<P>连接池的设置和应用 </P>
<P>我选择IBM公司的应用服务器平台WebSphere来给大家演示连接池的设置，使你面对友好的Web界面，可以体验到非常简易的操作场景。 </P>
<P>首先，我们进入WebSphere的管理控制台，这是一个非常漂亮的Web界面： </P>
<P><BR>紧接着，我选定一个数据源：Session Persistence datasource，就可看到这个数据源的属性配置了。在这儿，仅仅列举和连接池有关的属性： </P>
<P>Minimum Pool Size 池中保持的连接的最小数目；有新的请求，且没有激活连接可供使用时，池中连接数将增大，到最大连接数为止 <BR>Maximum Pool Size 池中保持的连接的最大数目；当这个数目达到，且没有激活连接可供使用时，新的请求将等待 <BR>Connection Timeout 当连接数达到最大值，且激活连接都在被使用时，新的请求等待的时间 <BR>Idle Timeout 连接可在池中闲置的时间；超过将释放资源，到最小连接数为止 <BR>Orphan Timeout 连接在被应用控制时，可闲置的时间；超过将返回池中 </P>
<P><BR>你可以根据需要来修改这些数值，以满足你的应用需要。接下来，我们讨论一下连接池的应用。 </P>
<P>EJB访问数据库（场景1，使用JDBC 1.0） import java.sql.*; <BR>import javax.sql.*; <BR>... <BR>public class AccountBean implements EntityBean { <BR>... <BR>public Collection ejbFindByLastName(String lName) { <BR>try { <BR>String dbdriver = new initialContext().lookup("java:comp/env/DBDRIVER").toString(); <BR>Class.forName(dbdriver).newInstance(); <BR>Connection conn = null; <BR>conn = DriverManager.getConnection("java:comp/env/DBURL", "userID", "password"); <BR>... <BR>conn.close(); <BR>} <BR>... <BR>} </P>
<P> </P>
<P> </P>
<P><BR>如果EntityBean是一个共享组件，那么每次客户请求时，都要建立和释放与数据库的连接，这成为影响性能的主要问题。 </P>
<P>EJB访问数据库（场景2，使用JDBC 2.0） import java.sql.*; <BR>import javax.sql.*; <BR>// import here vendor specific JDBC drivers </P>
<P>public ProductPK ejbCreate() { <BR>try { <BR>// initialize JNDI lookup parameters <BR>Context ctx = new InitialContext(parms); <BR>... <BR>ConnectionPoolDataSource cpds = (ConnectionPoolDataSource)ctx.lookup(cpsource); <BR>... <BR>// Following parms could all come from a JNDI look-up <BR>cpds.setDatabaseName("PTDB"); <BR>cpds.setUserIF("XYZ"); <BR>... <BR>PooledConnection pc = cpds.getPooledConnection(); <BR>Connection conn = pc.getConnection(); <BR>... <BR>// do business logic <BR>conn.close(); <BR>} <BR>... <BR>} <BR>EJB组件利用JNDI的lookup()方法定位数据库的连接池资源，利用getConnection()方法得到已经打开的数据库连接，而用close()来释放连接，放回池中。因此，与场景1相比，少了与数据库建立物理连接的损耗。对于原本要频繁打开和关闭物理连接的应用来说，通过这种建立逻辑连接并复用的方法，性能肯定能够得到大幅度提高。 </P>
<P>性能问题的深远思索 </P>
<P>性能问题并不局限于数据库的应用上，而是存在于每个软件系统中。我们希望软件系统付出的最小，而获得的最大，因而无时无刻不在优化它们。通过《Java程序访问数据库的速度瓶颈问题的分析和解决》和本文，我对Java程序访问数据库的性能问题做了分析，并提供了优化Java程序的解决方案，希望对你有所帮助。 </P>
<P>也许你会关心碰到类似的性能问题时应如何分析和解决，我就针对此次探讨"访问数据库的速度瓶颈"问题的过程中碰到的难题、网友的意见和自己的体会，作一个关于"方法论"的经验总结，希望能够抛砖引玉，更好地解决类似的问题。 </P>
<P>解决性能问题的几条经验 </P>
<P>不要让硬件的低配置成为软件正常运行的障碍，后者有升级前者的需求，请立即满足；经常碰到这样的问题"P166+64M的机子跑Win2K+MySql+JBoss，能跑么？"我在回答"可以"的同时只有对着屏幕发呆了。 <BR>尽量使用商业软件，并享受良好的售后技术支持；如果你没有黑客精神，请不要使用自由软件。 <BR>分析好自己的问题，也许它的本质和他人的不同；没有一把钥匙可打开的任一把锁。 <BR>确定瓶颈环节的位置；解决了瓶颈问题，往往整个的性能问题就解决了，千万不要抓着边缘的问题不放。 <BR>将瓶颈环节细分为多个顺序的流程，用逐个替代的方法来试探瓶颈的核心位置；细分问题使你都问题有更进一步的了解。 <BR>做自己能做的和该做的事情，始终面向自己的现实问题；不要尝试那些应该由厂商解决的问题（例如，自己写个JDBC驱动）。 <BR>他人的方案只供自己参考，解决要靠自己思考；你我应用的情形不同，应用解决方案要在理解他人的方案之后。 <BR>观察新技术，应用新技术；它往往包含了前人对问题解决的思路，只是对你来说不可见。 <BR>问题得到解决后，立即罢手，并汇报结果；在现实问题得到解决后，没有必要花费精力在非核心的问题上，也许它们永远不会被碰到，不要假想问题让自己解决。 <BR>上述经验为个人即兴的总结，并未有严谨的逻辑推导，仅代表我解决技术问题的思路，供你碰到类似问题时参考。 </P>
<P>可以提前告诉大家的是，下一篇文章我将把主题转移到JDO的介绍和讨论上。这个陌生的API已经进入Final Draft了，可它到底能够给数据存取带来什么好处，和JDBC有什么关联的地方，《JDO（Java Data Object）的发展和介绍》将向你娓娓道来。 </P>]]&gt;<img src ="http://www.blogjava.net/qq13367612/aggbug/16131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-19 21:17 <a href="http://www.blogjava.net/qq13367612/articles/16131.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OOP的未来</title><link>http://www.blogjava.net/qq13367612/articles/16026.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 15 Sep 2005 05:50:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16026.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16026.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16026.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16026.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16026.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 把宝押在Java上？或是坚持C++不动摇？还是考虑考虑C#？你是否犹豫不决？那么不妨先听听下面这些专家的说法，对于面向对象程序设计语言的现状和未来作一番了解。关键词：C++，Java，C#，OOP 科技进展一日千里，产品生产周期则越来越短，不少程序员已经完全没有时间掌握第二种语言。不论一个程序员的技术能力是否被考虑，对于他来说，将来能够参与什么类型的项目，加盟什么样的公司，在很大程度上...&nbsp;&nbsp;<a href='http://www.blogjava.net/qq13367612/articles/16026.html'>阅读全文</a><img src ="http://www.blogjava.net/qq13367612/aggbug/16026.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-15 13:50 <a href="http://www.blogjava.net/qq13367612/articles/16026.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对主流技术的分析和总结</title><link>http://www.blogjava.net/qq13367612/articles/16027.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 15 Sep 2005 04:58:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16027.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16027.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16027.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16027.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16027.html</trackback:ping><description><![CDATA[对主流技术的分析和总结<BR><BR>一、引言<BR>我为什么要写这篇文章<BR>首先，我要限定我文章的范围，我讨论的问题局限于桌面应用开发领域和企业应<BR>用开发领域，所以我的结论并不适用于整个软件开发界，比如我说C语言已经退出<BR>历史舞台，这对于写嵌入式系统的人和编写操作系统内核的人来说显然是错了。<BR>我写这篇文章的目的主要是：<BR>* 简单的介绍并评价当前主流技术<BR>* 比较当前的主流技术<BR>* 预计技术的演变<BR><BR>如果你想做程序员或者已经是个程序员，你可能会面对这些困惑：<BR>* 学什么语言呢？Delphi、C++、VB、Java、C#、PHP、Python?<BR>* 选择什么开发工具呢？Delphi、VC、C++Builder、JBuilder?<BR>当你已经入了门，有了一定的基础之后（可能已经通晓了几种语言），你会面临<BR>进一步的困惑：<BR>* MFC和VCL之间是什么关系？<BR>* J2EE到底是什么？.Net到底是什么？两者有什么本质的区别，我应该学习哪一个呢？<BR>* COM那么复杂，为什么很多地方都用到它？我必须学习它吗？<BR><BR><BR>如果是作为一个软件公司，如果不是那么大，如果你的公司还没有一个真正的技<BR>术上的灵魂人物，那么你也会面临同样的困惑。技术问题纷繁复杂，让你不知所<BR>从，而且真正的精通每一项技术都需要巨大的时间和人力的投入，你怎么办？选择<BR>哪种技术作为公司的主流技术呢？选择的方向是否正确是个关乎你的公司的生死存<BR>亡的问题。<BR><BR>你面临着这些困惑吗？如果是，那么请让我试着为你拨云见日。<BR><BR><BR>我的故事<BR><BR>在我上大学之前，我从没见过计算机。大学的时候，正是Dos和FoxBASE的年代，<BR>也正是计算机软件开发世界几件伟大的事情发上的时候：(Windows3.1、Borland<BR>C++ 3.1、Visual Basic 1.0 的推出也是伟大的事情，但那时候我还不知道计算机<BR>为何物)Widnows95推出，并开始应用；Visual Basic 5.0推出，开发工具中第一次<BR>出现了成熟的、被广泛应用的Auto Code Completion技术；Java推出；ASP技术开<BR>始盛行,Windows DNA技术被理解和接受；标准C++诞生;Visual C++6.0 推出；J2EE<BR>规范推出。<BR>成为一个程序员对我而言并不顺利，因为我不是科班出身。我入门的程序语言是<BR>FoxBASE，这让我一直对FoxBASE有种特殊的感情，我也正是通过Visual FoxPro3.0<BR>转写Windows程序的，如果没有它，我也许就不会成为一个程序员了。后来，在大<BR>学期间接触到了InterDEV，那是个写ASP程序的开发工具，还有Java，也是那时候<BR>接触的，当时有点盲目崇拜的意思（我想我喜欢Java的一个原因可能是刚开始学C<BR>的时候很受挫折）。毕业之后，我就是凭借着自己写的一个ASP网站找到了自己的<BR>第一份工作——说来惭愧，我从来也没有成为一个C程序员。我真正的熟悉Java是在<BR>我翻译了一本Java数据结构的书和写了一套完整的GIS系统之后（说起此事，我要<BR>感谢一下我的公司，但因为这些故事与本文的主题无关，所以这里就不多说了）。<BR>再后来，我自己学习了标准C++和COM技术。<BR>有点像履历表了是吗？提到这些，我只是希望作为读者的你能够了解一下我的知<BR>识体系，从而能够知道我大概要讲些什么；同时也希望你能够原谅我可能犯的错误<BR>——我在这里说的话，并不一定就是最后的结论，虽然“共同探讨”这个词几乎是粗制<BR>滥造的书的作者专用语了，但我在这里引用它是真诚的，我愿意看到你的反馈。<BR><BR><BR>要涉及的话题<BR>在开始文章的正题之前，我先大概地介绍这篇文章将会涉及到哪些知识。如果你<BR>是初学者，希望你不要被吓倒，这虽然是一篇技术文章，但我不会过多的讨论技术<BR>细节，如果你不懂我说的这些东西，也没关系，我本来就希望通过我的文章帮助你<BR>做出一个选择，不再走很多人已经走过的弯路，你这要记住结论就可以了，随着你<BR>知识的增长，以后你会渐渐明白；如果你已经通晓了这些技术或其中的大部分，那<BR>么我相信读了这篇文章你会有一些另外的收获。<BR>主流的程序设计语言： C++、Delphi(Object Pascal)、Java、C#<BR>桌面应用程序框架：MFC、VCL、QT、Java AWT\SWING、.Net<BR>企业应用程序框架：Windows DNA（ASP、COM、COM+）、J2EE、.Net Framework<BR>开发工具：Visual Basic、Delphi、Visual C++、C++ Builder 、Visual C#<BR><BR>二、正文 <BR>好了，现在让我们开始我的正文吧。<BR>首先，我来完成这篇文章的第一个目标：介绍并评价当前主流技术。我指的今天<BR>的主流技术是：<BR>* 程序设计语言：C++\Delphi（本来应该是Object Pascal，但为了简单，我就语<BR>言和工具混为一谈吧）\Java\C#(虽然他刚刚推出，但因为微软为之倾注了大量心<BR>血，一定会成为一种重要的开发语言)<BR>* 桌面应用程序框架:MFC\VCL<BR>* 企业应用程序框架:Windows DNA\J2EE\.Net<BR>* COM技术：我单独提出这项技术，是因为它无法简单的被视为语言、桌面应用程<BR>序框架或企业应用程序框架，它与这些都有关系。<BR>2.1 程序设计语言<BR>2.1.1 C++<BR>语言的演进最初要从二进制代码和汇编说起，但那太遥远了。我们就从面向过程的<BR>语言说起吧（包括Basic\C\Fortran\Pascal）。这种面向过程的高级语言终于把计<BR>算机带入了寻常的应用领域。其中的C语言因为它的简单和灵活造就了Unix和<BR>Windows这样的伟大的软件。面向对象的语言是计算机语言的一个合乎逻辑的进<BR>化，因为在没有过多的影响效率、简单性的前提下提供了一种更好的组织数据的方<BR>法，可以程序更容易理解，更容易管理——这一点可能会引出不同意见，但事实胜于<BR>雄辩，C++终于让C语言的领地越来越小，当今还活着的计算机语言或多或少的都具<BR>备面向对象的特征，所以这一点并不会引起太多困惑。C++的成功很大程度要归因<BR>于C，C++成为它今天的样子是合乎逻辑的产物。因为在面向过程的时代，C几乎已<BR>经统一天下了。今天著名的语言象Java\C#都从C借鉴了很多东西，C#本来的意思就<BR>是C++++。<BR>其实C++曾经很有理由统一面向对象程序设计语言的天下来着，但可惜的是，C++<BR>太复杂了。即使是一个熟练的程序员，要你很清楚的解释一些问题你也会很头痛。<BR>举几个还不是那么复杂的例子来说：<BR>对=的重载\成员转换函数\拷贝构造函数\转化构造函数之间有什么区别和联系呢？<BR>这样定义一个类成员函数private: virtual void MemFun() = 0; 是什么意义呢？<BR>int (*(*x(int))[4])(double); 是什么意思？<BR>还有其他的特征，比如说可以用来制造一种新语言的typedef和宏（虽然宏不是C+<BR>+的一部分，但它与C++的关系实在太密切了），让你一不小心就摔跤的内存问题<BR>（只要new 和delete就可以了吗？有没有考虑一个对象存放在容器中的情况？）……<BR>诸如此类，C++是如此的复杂以至于要学会它就需要很长的时间，而且你会发现即<BR>使你用C++已经好几年了，你还会发现经常有新东西可学。你想解决一个应用领域<BR>的问题——比如说从数据库里面查询数据、更改数据那样的问题，可是你却需要首先<BR>为C++头痛一阵子才可以，是的，你精通C++，你可以很容易的回答我的问题，可是<BR>你有没有想过你付出了多大的代价呢？我不是想过分的谴责C++，我本人喜欢C++，<BR>我甚至建议一个认真的开发普通的应用系统的程序员也去学习一下C++，C++中的一<BR>些特性，比如说指针运算\模板\STL几乎让人爱不释手，宏可以用几个字符代替很<BR>多代码，对系统级的程序员来说，C++的地位是不可替代的，Java的虚拟机就是C++<BR>写的。C++还将继续存在而且有旺盛的生命力。<BR><BR><BR>2.1.2 Java和C#<BR>Java和C#相对于C++的不同最大的有两点：第一点是他们运行在一个虚拟环境之<BR>中，第二点是语法简单。对于开发人员而言，在语法和语言机制的角度可以把Java<BR>和C#视为同一种语言。C#更多的是个政治的产物而不是技术产物。如果不是Sun为<BR>难微软的话，我想微软不会费尽心力推出一个和Java差不多的C++++，记得Visual<BR>J++吗，记得WFC吗？看看那些东西就会知道微软为Java曾经倾注了多少心血。而且<BR>从更广泛的角度来说，两者也是非常相似的——C#和Java面对的是同样的问题，面向<BR>应用领域的问题：事务处理、远程访问、Web service、Web页面发布、图形界面。<BR>那么在这一段中，我暂且用Java这个名字指代Java和C#两种语言——尽管两者在细节<BR>上确实有区别。<BR>Java是适合解决应用领域的问题的语言。最大的原因Java对于使用者来说非常简<BR>单。想想你学会并且能够使用Java需要多长时间，学会并且能够使用C++要多长时<BR>间。由于Java很大程度上屏蔽了内存管理问题，而且没有那么多为了微小的性能提<BR>升定义的特殊的内容（比如说，在Java里面没有virtual 这个关键字,Java也不允<BR>许你直接在栈上创建对象，Java明确的区分bool和整型变量），他让你尽量一致的<BR>方式操作所有的东西，除了基本数据类型，所有的东西都是对象，你必须通过引用<BR>来操作他们；除了这些之外，Java还提供了丰富的类库帮助你解决应用问题——因为<BR>它是面向应用的语言，它为你提供了多线程标准、JDBC标准、GUI标准，而这些标<BR>准在C++中是不存在的，因为C++并不是直接面向解决应用问题的用户，有人试图在<BR>C++中加入这些内容，但并不成功，因为C++本身太复杂了，用这种复杂的语言来实<BR>现这种复杂的应用程序框架本人就是一件艰难的事情，稍后我们会提到这种尝试——<BR>COM技术。渐渐的，人们不会再用C++开发应用领域的软件，象MFC\QT\COM这一类的<BR>东西最终也将退出历史舞台。 <BR><BR>2.1.3 Delphi<BR>Delphi是从用C++开发应用系统转向用Java开发应用系统的一个中间产物。它比C++<BR>简单，简单的几乎象Java一样，因为它的简单，定义和使用丰富的类库成为可能，<BR>而且Delphi也这么做了，结果就是VCL和其他的组件库。而另一方面，它又比运行<BR>于虚拟环境的java效率要高一些，这样在简单性和效率的平衡之中，Delphi找到了<BR>自己的生存空间。而且预计在未来的一段时间之内，这个生存空间将仍然是存在<BR>的。可以明显的看出，微软放弃了这个领域，他专注于两方面：系统语言C++和未<BR>来的Java(其实是.Net)。也许这对于Borland来说，是一件很幸运的事情。如果我<BR>能够给Borland提一些建议的话，那就是不要把Delphi弄得越来越复杂，如果那<BR>样，就是把自己的用户赶到了C++或Java的领地。在虚拟机没有最终占领所有的应<BR>用程序开发领域之前，Delphi和Delphi的用户仍然会生存得很好。<BR><BR><BR>2.2 桌面应用程序框架<BR>目前真正成功的桌面应用程序框架只有两个，一个是MFC，一个是VCL，还有一些<BR>其他的，但事实上并未进入应用领域。遗憾的是我对两个桌面应用程序框架都不精<BR>通。但这部不妨碍我对他做出正确的评价。<BR>2.2.1 MFC<BR>MFC（还有曾经的OWL）是SDK编程的正常演化的结果，就象是C++是C的演化结果一<BR>样。MFC本身是一件了不起但不那么成功的作品，而且它过时了。这就是我的结论。<BR>MFC凝聚了很多天才的智慧——当然，OWL和VCL也一样，侯捷的《深入浅出MFC》把这些<BR>智慧摆在了我们的面前。但是这件东西用起来估计不会有人觉得很舒服，如果你一<BR>直在用Java、VB或者Delphi，再回过头来用MFC，不舒服的感觉会更加强烈。我不<BR>能够解释MFC为什么没有能够最终发展成和VCL一样简单好用的桌面程序框架，也许<BR>是微软没精力或者没动力，总之MFC就是那个样子了，而且也不会再有发展，它已<BR>经被抛弃了。我有时候想，也许基于C++这种复杂的语言开发MFC这样的东西本身就<BR>是错误的——可以开发这样的一个框架，但不应当要求使用它的人熟悉了整个框架之<BR>后才能够使用这个系统，但很显然，如果你不了解MFC的内部机制，是不太可能把<BR>它用好的，我不能解释清楚为什么会出现这种现象。<BR>2.2.2 VCL <BR>相比之下VCL要成功的得多。我相信很多使用VCL的人可能没有像MFC的用户研究MFC<BR>那样费劲的研究过VCL的内部机制。但这不妨碍他们开发出好用好看的应用程序，<BR>这就足够了，还有什么好说的呢？VCL给你提供了一种简单一致的机制，让你可以<BR>写出复杂的应用程序。在李维的Borland故事那篇文章中曾经说过，在Borland<BR>C++3.1推出之后Borland就有人提出开发类似C++ Builder一类的软件，后来竟未成<BR>行。是啊，如果C++Builder是在那个时候出现的，今天的软件开发领域将会是怎么<BR>样的世界呢？真的不能想象。<BR>也许再过一段时间，这些都将不再重要。因为新生的语言如Java和C#都提供了类<BR>似于VCL的桌面应用程序框架。那个时候，加上Java和C#本身的简单性，如果他们<BR>的速度有足够块，连Delphi这种语言也要消失了，还有什么好争论的呢？只是对于<BR>今天的桌面程序开发人员来说，VCL确实是最好的选择。<BR>2.3企业应用程序框架<BR>2.3.1 Windows DNA<BR>Windows DNA的起源无从探究了。随着.Net的推出，事实上Windows DNA将成为历<BR>史的陈迹。Windows DNA虽然是几乎所有的企业应用程序开发人员都知道的一个名<BR>词，但我相信Windows DNA事实上应用的最广泛的是ASP而不是COM+。真正的COM开<BR>发有多少人真正的掌握了呢，更不要提COM+(我有必要解释一下：COM+是COM的执行<BR>环境，它提供了一系列如事务处理、安全等基础服务，让应用程序开发人员尽量少<BR>在基础架构设计上花精力)——当然我这里指的COM开发不是指VB下的COM开发，所以<BR>要这么说，是因为我觉得如果不能理解用C++进行COM开发，也就不能真正的理解<BR>COM技术。如果以一种技术没有被广泛理解和应用作为失败的标志，那么Windows<BR>DNA实际上是失败了，但这不是它本身的错，而是基于C++的COM技术的失败造成<BR>的。多层应用、系统开发角色分离的概念依然没有过时。<BR>2.3.2 J2EE<BR>J2EE是第一套成功的企业应用程序开发框架。因为它把事务处理、远程访问、安全<BR>这些概念带入了寻常百姓家。这种成功我认为要归因于Java的简单性。Java的简<BR>单，对于J2EE容器供应商来说一样重要。开发一个基于Java的应用服务器要比基于<BR>C++的更容易。而且由于Java的简单性，应用系统开发者出错的机会也会少一些，<BR>不会像C++的开发者那样受到那么多挫折。开发基于Java的企业应用系统的周期会<BR>更短，这恐怕是不容争辩的事实。不论如何，是J2EE让事务处理、远程访问、安全<BR>这些原来几乎都是用在金融系统中的概念也被一般的企业用户使用并从中获得利益。<BR>2.3.3 .NET<BR>.Net有什么好说的呢？其实，它不过是微软的J2EE。事务处理、安全、远程访<BR>问，这些概念在.Net中都找得到。更有力的说明是，微软也利用了.Net实现了一个<BR>Pet Store。所以，.Net与J2EE几乎是可以完全对等的。但微软确实是一家值得尊<BR>敬的公司——我指从技术上，象Web form这种东西，我相信很多Web应用开发者都梦<BR>想过甚至自己实现过，但Sun却一直无动于衷，而且似乎Borland也没有为此作过太<BR>多努力，好像有过类似的东西，但肯定是不怎么成功——Borland已经很让人敬佩<BR>了，我们也许无法要求太多。<BR>2.4 COM技术<BR>COM应当是个更广泛的词汇，其实我觉得AxtiveX、OLE、Automation、COM+都应当<BR>提及，因为如果你不理解COM，上面的东西你是无法理解的。而且，我只是想说明<BR>COM是一种即将消亡的技术，仅仅说说COM的复杂性和他的问题就够了，所以不会提<BR>及那些东西。<BR>为什么会出现COM技术？COM的根本目标是实现代码的对象化的二进制重用，进而实<BR>现软件的组件化、软件开发工作的分工。这要求他做到两点：第一，能够跨越不同<BR>的语言，第二，要跨越同一种语言的不同编译器。COM技术为这个目标付出了沉重<BR>的代价，尤其是为了跨越不同的编译器，以至于无论对于使用者而言还是开发者而<BR>言，他都是一个痛苦的技术。但幸运的事，这一切终归要结束了。<BR>让我们从这个目的出发看看COM为什么会成为它现在的样子。<BR>其实COM不是什么新玩意，最初的DLL就是重用二进制代码的技术。DLL在C的年代可<BR>能还不错，但到了C++的年代就不行了。原因在于如果你在.h文件中改变了类定义<BR>（增加或者减少了成员变量），代码库用户的代码必须重新编译才可以，否则用户<BR>的代码会按你的旧类的结构为你的新类分配内存，这将产生什么后果可想而知。这<BR>就是为什么通过接口继承和通过接口操作对象成为COM的强制规范的原因，能够通<BR>过对象的方式组织代码是COM的重要努力。<BR>那么著名的IUnknown 接口又是怎么回事呢？这是为了让使用不同编译器的C++开发<BR>人员能够共享劳动成果。首先看QueryInterface，因为COM是基于接口的，那么一<BR>个类可能实现了几个接口，而对于用户来说，你又只能通过操作接口来操作类，这<BR>样你必须把类造型成某个特定的接口，使用Dynamic_cast吗？不行，因为这是编译<BR>器相关的，那么，就只好把它放在类的实现代码中了，这就是QueryInterface的由<BR>来。至于AddRef和Release，他们产生的第一个原因是delete这个操作要求一个类<BR>具有虚析构函数（否则的话，他的父类的析构函数将不会被调用），但不幸的是不<BR>同的编译器中虚析构函数在vtbl中的位置并不相同，这就是说你不能让用户直接调<BR>用delete，那么你的COM对象必须提供自己删除自己的方法；另外的原因在于一个<BR>COM对象可能作为几个接口在被用户同时使用，如果用户粗暴的删掉了某个接口，<BR>那么其他的接口也就不能用了，这样，只好要求用户在想用一个接口的时候通过<BR>AddRef通知COM对象“我要使用你了，你多了一个用户”，而在不用之后要调用<BR>Release告诉COM对象“我不用你了，你少了一个用户”，如果COM对象发现自己没有<BR>用户了，它就把自己删掉。<BR>再看看诡异的HRESULT，这是跨语言和跨编译器的代价。其实，异常处理是物竞天<BR>择的结果——连一直用效率作标榜的C++都肯牺牲效率来实现这个try-catch，可见它<BR>的意义，但COM为了照顾那些低级的语言居然抛弃了这个特征——产生的结果就是<BR>HRESULT。我们可以看看他是多么愚蠢的东西。首先，我们要通过一个整数来传递<BR>错误信息，通过IErrorInfo来查找真正的错误描述，本来在现代语言中一个try-<BR>catch可以解决的问题，现在我们却需要让用户和开发者都走很多路才能解决，你<BR>怎么评价这个结果？其次，由于这个返回计算结果的位置被占用了，我们不得不用<BR>怪异的out参数来返回结果。想想一个简单的 int add(int op1,int op2)在COM中<BR>竟然要变成HRESULT add(int op1,int op2,int *result)，我不知道你对此作何感<BR>想。而且由于COM的方法无法返回一个对象而只能返回一个指针，为了完成一个简<BR>单的std::string GetName()这一类的操作，你要费多少周折——你需要先分配一块<BR>内存空间，然后在COM实现中把一个字符串拷贝到这个空间，用完了你要删掉这个<BR>空间，不知道你是否觉得这种工作很幸福，反正我觉得很痛苦。<BR>还有COM为了照顾那些解释性的语言，又加入了Automation技术，你有没有为此觉<BR>得痛苦？本来一个简单的方法调用，现在却需要传给你一个标志变量，然后让你根<BR>据这个标志变量去执行相应的操作。（这一点我现在仍然不明白，为什么解释性的<BR>语言需要通过这个方式来执行一个方法）。<BR>“我受够了，我觉得头痛”，你会说，是啊，我想所有的人都受够了，所有这些因素<BR>实际上是把COM技术变成了一头让人无法驾驭的怪兽。人对复杂事物的掌控能力终<BR>究是有限的，C++本身已经够复杂了， COM的复杂性已经超出了我们大部分人的控<BR>制能力，你需要忍受种种痛苦得到的东西与你付出的代价相比是不是太不值得了？<BR>我们学习是为了解决问题，而现在我们却需要为了学习这件事情本身耗费太多的精<BR>力。这些痛苦的东西太多了，我在这里说到的，其实只是一小部分而已。计算机技<BR>术是为人类服务的，而不是少数人的游戏（我想COM技术可能是他的设计者和一部<BR>分技术作者的游戏），难道我们愿意成为计算机的奴隶吗？通过一种过于复杂的技<BR>术抛弃所有的人其实等于被所有的人抛弃，这么多年中选择J2EE的人我相信不乏高<BR>手，你是不是因为COM的过于复杂才选择J2EE的？因为它可以用简单的途径实现差<BR>不多的目标——软件的“二进制”重用、组件化、专业分工（容器开发商和应用开发商<BR>的分工）。事实上，你是被微软所抛弃的，同时，你也抛弃了微软。<BR>现在让我们回来吧，我把你带进了COM的迷宫，现在让我把你领回来。再让我们看<BR>看COM到底想实现什么目标，其实很简单，不过是代码的二进制重用，也就是说你<BR>不必给别人源代码，而且你的组件可以象计算机硬件那样“即插即用”。我们回过头<BR>来看看Java，其实，这种二进制重用的困难是C++所带来的（这不是C++本身的错，<BR>而是静态编译的错），当Java出现的时候，很多问题已经不存在了。你不需要一个<BR>头文件，因为Java的字节码是自解释的，它说明了自己是什么样子的，可以做什么<BR>事情，不像C++那样需要一个头文件来解释这些事情；也不需要事先了解对象的内<BR>存结构，因为内存是在运行的时候才分配的。<BR>如果我们现在再回过头来解决COM要解决的问题，你会怎么做呢？首先你会不再选<BR>择C++这种语言来实现代码的“二进制”重用，其次，你会把所有的语言编译成同样<BR>的“二进制” 代码（实际上，应当说是字节码），这显然是更聪明的做法，从前COM<BR>试图在源代码的级别抹平二进制层次的差异，这实际上是让人在做本来应当由机器<BR>做的事情，很愚蠢是吗？但他们一直做了那么多年，而且把这个痛苦带给了整个计<BR>算机世界——因为他们掌握着事实的标准，为了用户，为了利润，为了能够在<BR>Windows上运行，尽管你知道你在做着一个很不聪明的事情，但你还是做了。<BR>COM技术为了照顾VB这个小兄弟和实现统一二进制世界的野心，实在浪费了太多的<BR>精力。首先，落后的东西的消亡是必然的，就象C、Pascal在应用领域的消亡一<BR>样，Basic一点一点的改良运动是不符合历史潮流的做法，先进的东西和落后的东<BR>西在一起，要么先进的东西被落后的东西拖住后腿，要么是同化落后的东西，显然<BR>我们更愿意看见后者，现在Basic终于被现代的计算机语言同化了。其次，统一二<BR>进制世界好像不是那么简单的事情，而且也没什么必要，微软的COM技术奋战了10<BR>年，现在也只有他自己和Borland支持，.Net终于放弃了这个野心。这一切终于要<BR>结束了。过去J2EE高歌猛进地占领着应用开发的领地，COM在这种进攻面前多少显<BR>得苍白无力。现在微软终于也有了可以和J2EE一较长短的.NET，对于开发人员来<BR>讲，基于字节码的组件的二进制重用现在是天经地义的事情；你不用再为了能够以<BR>类方式发布组件做那么多乱七八糟的事情，因为现在所有的东西本来就是类了；实<BR>现软件开发的分工也很自然，你是专业人员，就用C#吧，你是应用开发人员，你喜<BR>欢用VB.Net，你就用吧，反正你们写的东西最终都被翻译成了一样的语言（其实我<BR>觉得这一点意义不大，因为一些不那么好用的语言被淘汰是正常的事情，C风格成<BR>为程序设计语言的主流风格，肯定是有它的原因的，语言的统一其实是大势所趋，<BR>就象中国人民都要说普通话一样，所以我觉得Java不支持多语言算不上缺点——本来<BR>就是一种很好看很好用的语言了，为什么要把简单问题复杂化呢？）。<BR>COM不会在短期内死去，因为我估计微软还不会马上用C#开发Office和Windows的图<BR>形界面部分，但我相信COM技术退出历史舞台的日子已经不远了，作为一般的开发<BR>人员，忘了这段不愉快的历史吧——你将不会在你的世界里直接和COM打交道。<BR>若干年以后，我想COM也许会成为一场笑话，用来讽刺那种野心过大、钻牛角尖的<BR>愚蠢的聪明人。<BR><BR>结论<BR><BR>说了很多，应该是有个结论的时候了。我似乎做了一件冒天下之大不韪的事情，<BR>因为我评价了主流技术，还要试图进行比较，好像某个名人说过：“C++Builder也<BR>好，Visual C++也好，能在市场上立足，肯定都是有自己的过人之处的，而且一个<BR>人精通数种开发语言、数种开发工具是不可能的事情”，言下之意就是说你不要对<BR>这些东西妄加评论，但怎么可以不评论？好像高手都不屑于说哪种语言好、哪种语<BR>言坏，我不知道什么时候形成了这种风气。简单地说C++比Java好或者Java比C++好<BR>显然是愚蠢的行为，但如果让你用Java写驱动程序，用C++开发一个MIS系统是不是<BR>愚蠢的行为呢？显然更愚蠢。我想，作为一个独立的能思考的人，外界的东西对你<BR>而言总是有好有坏，而且你的生命有限，你还要享受你的生活，所以你必须做出选<BR>择。所以，起码评价这些东西对我自己而言是正确的——只要我有能力评价，那我就<BR>说出我的评价吧。<BR><BR>对于计算机语言来说，未来真正重要的语言只有三种C++、Java和C#。<BR>C++将更适合于专业的计算机公司编写图形界面系统（比如KDE）、虚拟机（比如<BR>Java虚拟机或者.Net运行环境）、杀毒软件或者其他的盒装软件（比如说<BR>Photoshop、Dream weaver）这一类的东西。而且C++适合程序员做学习之用，能对<BR>C++有一定理解的程序员往往也应该能更深刻的理解Java、C#，这有助于编写更好<BR>的软件。<BR>如果是开发为客户定制的应用系统Java、C#是更好的选择。包括桌面应用和Web应<BR>用。至于其他的语言比如VB.Net其实并没有太大的意义。<BR>Delphi在未来一段时间将继续存在，而且活得不错。最重要的原因在于，第一它有<BR>一套丰富的组件库，第二它效率相对算高的，第三它简单。如果虚拟机的执行效率<BR>赶不上Delphi，它就有存在的理由，但从过去的Visual J++来看，微软的虚拟机做<BR>得确实可以很好，加上.Net的组件库和C#的简单性并不差，所以从长远来看Delphi<BR>可能不那么乐观。<BR>其实上面的比较也包含了桌面应用程序框架的比较。在现在来说VCL无疑最好的，<BR>MFC已经退出历史舞台。.Net中所带的桌面应用程序框架将最终统一桌面应用领域<BR>（但不包括微软和Borland这样的专业公司，因为他们要作出最好用而且效率最高<BR>的软件）。至于Java中所带的桌面应用程序框架，实在让人哭笑不得。这个结论的<BR>变数是.Net运行环境的执行效率。如果.Net中的虚拟机象Java的一样，那微软就倒<BR>霉了，它等于放弃了桌面应用开发工具领域，但根据微软在Visual J++上的成就和<BR>他推广.Net的背水一战的驾式，.Net在桌面领域失败的可能性不大（但不是没有，<BR>起码基于COM的技术在企业应用领域几乎可以说是全军覆没）。<BR>在企业应用程序框架领域，最终只有J2EE和.Net可以决一雌雄。我没有提到<BR>CORBA，它也可以算是企业应用程序框架，但他的声势和J2EE或者.NET实在不能同<BR>日而语，而且最重要的是只有Borland一家大公司支持它（SUN、IBM、BEA、<BR>Borland支持J2EE，微软就不用说了），所以就不详细说他了。<BR>那么最终谁会赢呢？我想微软赢的可能性大一些。这样说可能让很多人不快，而且<BR>IBM的厉害也是有目共睹的。但这并不是纯技术问题，就象Windows NT蚕食Unix的<BR>领土那样，那时候微软也是孤军作战。J2EE的问题在于第一：混乱，第二，价高。<BR>我相信很多人都对这两点有过不快的经历。你知道写给Weblogic的应用程序不是很<BR>顺利地就可以移植到Websphere上的，反过来也一样。但.Net就不一样了，那是微<BR>软一个人的作品，根本不存在移植的问题。如果J2EE阵营不想败在这一点上，有三<BR>个办法，第一种就是通过制定统一的标准彻底消灭移植问题，第二种是开发一种好<BR>用的部署工具（不能象JBuilder那么大、那么慢：），屏蔽不同的应用程序容器之<BR>间的区别，第三种，也是最不可能的，就是J2EE阵营有人能够一统天下。显然，这<BR>三种解决办法都不太现实。<BR>第二点价高，这是SUN、IBM、BEA、ORACLE传统，也是它们一直让微软的进攻屡屡<BR>得手的软肋。我一直不太能明白他们的西就为什么那么贵。这样想一想：微软的<BR>.Net SDK白送给你，BEA的Weblogic一个CPU的License两万，如果你两种技术都<BR>会，如果你给客户的系统报价一样，你选哪种开发技术？这一点实在让人觉得无可<BR>奈何。J2EE有的东西，.Net也有（除了不能跨平台），技术上的细微差别在巨大的<BR>价格差异面前还有什么意义呢？<BR>当然，SUM、IBM这些大公司也不是等闲之辈。就象Windows NT没有消灭Unix一样，<BR>J2EE应当会像Windows NT和Unix的共存一样和.Net共存，只是我想.Net恐怕会占上<BR>风。 <BR>闲话<BR>说完了该说的技术问题，说说闲话吧。有的话放在心里觉得不说出来不舒服，且让<BR>我一吐为快:)<BR>给入门程序员的建议<BR>不知道我在学计算机的时候是不是走了弯路。但我想如果让我重新开始学写程序<BR>的话，我会采用一些不同的办法。如果你也正在想成为一个程序员，这些也许会对<BR>你有帮助。<BR>我觉得可能大概要分几个阶段，第一个阶段应该是找一门简单的语言入门，比如<BR>Java或者C#都应该比较合适，选一本简单的带例子的书（最好不要太厚），按部就<BR>班的把书学完。这时候可能还有些懵懵懂懂，但没关系，可以开始做个小小的软件<BR>了，重要的事你要自己用那种语言的方式想思考，如果有项目做，当然更好。之<BR>后，你会觉得有点感觉了。如果你象我一样不是科班出身的，接下来应当补习一下<BR>计算机专业的课程，我觉得最重要的是数据结构——那些东西你可能永远都不会自己<BR>做，C++中有漂亮的STL，Java中也为你实现了大部分东西，但我觉得真的有必要学<BR>习那些内容，这会加强你用计算机语言思考问题的能力。在进一步，如果你的入门<BR>语言不是C++，那你可以补习一下C++，尽管你可能永远都不会用C++开发程序。C++<BR>在现在的计算机世界就象是普通话一样，而且它能让你很容易的理解其他语言中难<BR>以理解的问题。<BR>学完了C++，那你应当就已经不是一个初级程序员了，欢迎你进入计算机软件开发<BR>的世界。<BR><BR>印度的软件业<BR>我记得好像在CSDN上看见过一篇文章，极力的鼓吹印度的软件业。而且我记得他<BR>好像说过一句很刻薄的话“我们公司那些B大的和T大的，一个一个特别牛，牛得看<BR>不见人……做起界面极尽奇迹淫巧之能事……”，诸如此类，总之认为程序员只有象印<BR>度的高中生那样乖乖的、懂得UML、会看Function Specification才算是真正的程<BR>序员。我当时觉得很不舒服。我想这个人应该不是B或T大的——哦，别误会，我也不<BR>是——但我觉得好像B大的T大的人没象他说的那样。而且我不明白为什么中国的软件<BR>业为什么一定要向印度看齐？作为一家公司，你想获取商业利润，学习印度无可厚<BR>非，你大可以找一大堆高中生培训成编程蓝领（我没有轻视高中生的意思，我相信<BR>有很多“高中生”在技术领域取得的成就是让我望尘莫及的），但你不应该因此就把<BR>有血有肉有个性的程序员扁得一钱不值。说实话，所谓的编程蓝领不过是工厂里面<BR>的装配工，如果有一天工厂里面换了自动化的设备，这些人就全成了废人！而你要<BR>知道并不是这些装配工发明了自动化机器。我想这种话用不着多说，子曰“过犹不<BR>及”，你可以喜欢变成蓝领，但不要把问题推向极端，把问题推向极端往往就会犯<BR>错误。我们中国可以在某种程度上学习印度，但好像我们更应该学习美国——只是我<BR>们现在没那么富裕——可是微软也不是从一开始就是这样一个伟大的帝国的，IBM也<BR>一样。<BR><BR><BR><BR>附录 <BR>参考书目<BR>阅读如下图书有助于理解本文内容。而且这些书都是好书，值得一读。<BR>* 《标准C++宝典》<BR>* 《C++编程思想》<BR>* 《深入浅出MFC2e》<BR>* 《COM技术内幕》<BR>* 《COM本质论》<BR>* 《Java编程思想》<BR>* 《精通EJB》第二版<BR>* 《J2EE编程指南》<BR>* 《Delphi 6开发人员指南》<BR>* 《C#宝典》<BR>* 《微软 .Net战略》<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/16027.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-15 12:58 <a href="http://www.blogjava.net/qq13367612/articles/16027.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++之父 B. Stroustrup 近期言论</title><link>http://www.blogjava.net/qq13367612/articles/16221.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 15 Sep 2005 04:54:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16221.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16221.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16221.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16221.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16221.html</trackback:ping><description><![CDATA[<FONT color=#ff0000>[译者按] Bjarne Stroustrup博士，1950年出生于丹麦，先後毕业于丹麦阿鲁斯大学和英国剑挢大学，AT&T大规模程序设计研究部门负责人，AT&T 贝尔实验室和ACM成员。1979年，B. S开始开发一种语言，当时称为"C with Class"，後来演化为C++。1998年，ANSI/ISO C++标准建立，同年，B. S推出其经典着作The C++ Programming Language的第三版。C++的标准化标志 B. S博士倾20年心血的伟大构想终於实现。但是，计算技术的发展一日千里，就在几年前人们还猜想C++最终将一统天下，然而随 Internet的爆炸性增长，类似Java C#等新的 现代感十足的语言咄咄逼人，各种Script语言更是如雨後春笋纷纷涌现。在这种情况下，人们不禁有些惶恐不安。C++是不是已经过时了呢？其前景如何？标准C++有怎样的意义？应该如何学习？我们不妨看看B. S对这些问题的思考。以下文字是译者从Stroustrup1998年之後发表的若干文章 谈话笔记中精选出来的，由於出处不一，内容多有重复，为保持完整，亦一并译出。</FONT>
<H2>以下内容选自B. S在自己主页上发表的FAQ</H2>
<H3><FONT color=#0000ff>1. 请谈谈C++书。</FONT></H3>
<P>没有，也不可能有一本书对於所有人来说都是最好的。不过对於那些真正的程序员来说，如果他喜欢从"经典风格"的书中间学习一些新的概念和技术，我推荐我的The C++ Programming Language, 1998年的第三版和特别版。那本书讲的是纯而又纯的C++，完全独立於平台和库（当然得讲到标准库）。该书面向那些有一定经验的程序员，帮助他们掌握C++，但不适合毫无经验的初学者入门，也不适合那些临时程序员品尝C++快餐。所以这本书的重点在於概念和技术，而且在完整性和精确性上下了不少功夫。如果你想知道为什麽C++会变成今天的模样，我的另一本书 The Design and Evolution of C++ 能给你满意的答案。理解设计的原则和限制能帮助你写出更好的程序。www.accu.org是最好的书评网站之一，很多有经验的程序员在此仗义执言，不妨去看看。</P>
<H3><FONT color=#0000ff>2. 学习C++要花多长时间？</FONT></H3>
<P>这要看你说的"学习"是什麽意思了。如果你是一个Pascal程序员，你应该能很快地使你的C++水平达到与Pascal相近的程度；而如果你是一个C程序员，一天之内你就能学会使用C++进行更出色的C风格编程。另一方面，如果你想完全掌握C++的主要机制，例如数据抽象，面向对象编程，通用编程，面向对象设计等等，而此前又对这些东西不很熟悉的话，花上个一两年是不足为奇的。那麽是不是说这就是学习C++所需要的时间呢？也许再翻一番，我想打算成为更出色的设计师和程序员最起码也要这麽长的时间。<FONT color=#8080ff>如果学习一种新的语言不能使我们的工作和思想方式发生深刻<BR>的变革，那又何苦来哉？跟成为一个钢琴家或者熟练掌握一门外语相比，学习一种新的 不同的语言和编程风格还算是简单的。</FONT></P>
<H3><FONT color=#0000ff>3. 了解C是学习C++的先决条件吗？</FONT></H3>
<P>否 C++中与C相近的子集其实比C语言本身要好学，类型方面的错误会少一些，也不像C那样绕圈子，还有更好的支持库。所以应该从这个子集开始学习C++。<BR></P>
<H3><FONT color=#0000ff>4. 要想成为真正的OO程序员，我是不是得先学习Smalltalk？</FONT></H3>
<P>否。如果你想学Smalltaok，尽管去学。这种语言很有趣，而且学习新东西总是一个好主意。但是Smalltalk不是C++，而且把Smalltalk的编程风格用在C++里不会有什麽好结果。如果你想成为一个出色的C++程序员，而且也没有几个月的时间百无聊赖，请你集中力量学好C++以及其背後的思想。<BR></P>
<H3><FONT color=#0000ff>5. 我如何开始学习C++？</FONT></H3>
<P>这取决于你的基础和学习动机。如果你是个初学者，我想你最好找个有经验的程序员来帮助你，要不然你在学习和实践中不可避免的犯下的种种错误会大大地打击你的积极性。另外，即使你的编译器配备了充足的文档资料，一本C++书籍也永远是必不可少的，毕竟文档资料不是学习编程思想的好教材。<BR><BR><FONT color=#8080ff>选择书籍时，务必注意该书是不是从一开始就讲授标准C++</FONT>，<FONT color=#8080ff>并且矢志不渝地使用标准库机制。</FONT>例如，从输入中读取一个字符串应该是这样的：<BR><BR>    string s;    // Standard C++ style<BR>    cin >> s;<BR><BR>而不是这样的：<BR><BR>    char s[MAX];    /* Standard C style */<BR>    scanf("%s",s);<BR><BR>去看看那些扎实的C++程序员们推荐的书吧。记住，没有哪本书对所有人来说都是最好的。另外，要写地道的C++程序，而避免用C++的语法写传统风格的程序，新瓶装旧酒没多大意义。<FONT color=#ff0000>（遗憾的是，目前在市面上的中文C++教材中，符合B. S的这个标准的可以说一本都没有，大家只好到网上找一些英文的资料来学习了。--译者）</FONT><BR></P>
<H3><FONT color=#0000ff>6. 怎样改进我的C++程序？</FONT></H3>
<P>不好说。这取决于你是怎麽使用该语言的。<FONT color=#8080ff>大多数人低估了抽象类和模板的价值，反过来却肆无忌惮地使用造型机制(cast)和宏</FONT>。这方面可以看看我的文章和书。抽象类和和模板的作用当然是提供一种方便的手段建构单根的类层次或者重用函数，但更重要的是，它们作为接口提供了简洁的 逻辑性的服务表示机制。<BR></P>
<H3><FONT color=#0000ff>7. 语言的选择是不是很重要？</FONT></H3>
<P><FONT color=#8080ff>是，但也别指望奇迹</FONT>。很多人似乎相信某一种语言能够解决他们在系统开发中遇到的几乎所有问题，他们不断地去寻找完美的编程语言，然後一次次的失败，一次次的沮丧。另外一些人则将编程语言贬为无关紧要的细节，把大把大把的银子放在开发流程和设计方法上，他们永远都在用COBOL C和一些专有语言。一种优秀的语言，例如C++，能帮助设计者和程序员做很多事情，而其能力和缺陷又能够被清楚地了解和对待。</P>
<H3><FONT color=#0000ff>8. ANSI/ISO标准委员会是不是糟蹋了C++？</FONT></H3>
<P>当然不是 他们（我们）的工作很出色。你可以在一些细节上找些歪理来挑刺，但我个人对於这种语言以及新的标准库可是欣欣然。ISO C++较之C++的以前版本更出色更有条理。相对於标准化过程刚刚开始之初，你今天可以写出更优雅 更易于维护的C++程序。新的标准库也是一份真正的大礼。由於标准库提供了strings, lists, vectors, maps以及作用于其上的基本算法，使用C++的方式已经发生了巨大的变化。<BR></P>
<H3><FONT color=#0000ff>9. 你现在有没有想删除一些C++特性？</FONT></H3>
<P>没有，真的。<FONT color=#8080ff>问这些问题的人大概是希望我回答下面特性中的一个：多继承 异常 模板和RTTI</FONT>。但是没有它们，C++就是不完整的。在过去的N年中，我已经反复考虑过它们的设计，并且与标准委员会一起改进了其细节，但是没有一个能被去掉又不引起大地震。<BR><BR>从语言设计的角度讲，我最不喜欢的部份是与C兼容的那个子集，但又不能把它去掉，因为那样对於在现实世界里工作的程序员们来说伤害太大了。C++与C兼容，这是一项关键的设计决策，绝对不是一个叫卖的噱头。<FONT color=#8080ff>兼容性的实现和维护是十分困难的，但确实使程序员们至今受益良多。</FONT><BR><BR>但是现在，C++已经有了新的特性，程序员们可以从麻烦多多的C风格中解脱出来。例如，使用标准库里的容器类，象vector, list, map, string等等，可以避免与底层的指针操作技巧混战不休。<BR></P>
<H3><FONT color=#0000ff>10. 如果不必和C兼容，你所创造的语言是不是就会是Java?</FONT></H3>
<P>不是，差得远。如果人们非要拿C++和Java来作比较，我建议他们去阅读The Design and Evolution of C++，看看C++为什麽是今天这个样子，用我在设计C++时遵从的原则来检验这两种语言。这些原则与SUN的Java开发小组所持的理念显然是不同的。除了表面语法的相似性之外，C++与Java是截然不同的语言。在很多方面，Java更像Smalltalk<FONT color=#ff0000>（译者按：我学习Java时用的是Sun的培训教材，里面清楚地写道：Java在设计上采用了与C++相似的语法，与Smalltalk相似的语义。所以可以说Java与C++是貌合神离，与Smalltalk才是心有灵犀）</FONT>。Java语言相对简单，这部分是一种错觉，部份是因为这种语言还不完整。<FONT color=#8080ff>随 时间的推移，Java在体积和复杂程度上都会大大增长。在体积上它会增长两到三倍，而且会出现一些实现相关的扩展或者库。这是一条每个成功的商业语言都必须走过的发展之路。</FONT>随便分析一种你认为在很大范围内取得了成功的语言，我知道肯定是无有例外者，而且实际上这非常有道理。<BR><BR>上边这段话是在Java 1.1推出之前写的。<FONT color=#8080ff>我确信Java需要类似模板的机制，并且需要增强对於固有类型的支持。</FONT>简单地说，就是为了基本的完整性也应该做这些工作。另外还需要做很多小的改动，大部份是扩展。1998年秋，我从James Gosling<FONT color=#ff0000>（Java语言的创始人--译者）</FONT>那里得到一份建议书，说是要在Java中增加固有类型 操作符重载以及数学计算支持。还有一篇论文，是数学分析领域的世界级大师，伯克利大学的W. Kahan教授所写的How Java's Floating-Point Hurts Everyone Everywhere<FONT color=#ff0000>（"且看Java的浮点运算如何危害了普天下的芸芸众生"--译者）</FONT>，揭露了Java的一些秘密。<BR><BR>我发现在电视和出版物中关於Java的鼓吹是不准确的，而且气势汹汹，让人讨厌。大肆叫嚣凡是非Java的代码都是垃圾，这是对程序员的侮辱；建议把所有的保留代码都用Java重写，这是丧心病狂，既不现实也不负责任。Sun和他的追随者似乎觉得为了对付微软罪恶的"帝国时代"，就必须如此自吹自擂。但是侮辱和欺诈只会把那些喜欢使用不同编程语言的程序员逼到微软阵营里去。<BR><BR><FONT color=#8080ff>Java并非平台无关，它本身就是平台</FONT>。跟Windows一样，它也是一个专有的商业平台。也就是说，你可以为Windows/Intel编写代码，也可以为Java/JVM编写代码，<FONT color=#8080ff>在任何一种情况下，你都是在为一个属於某个公司的平台写代码，这些代码都是与该公司的商业利益扯在一起的。</FONT>当然你可以使用任何一种语言，结合操作系统的机制来编写可供JVM执行的程序，但是JVM之类的东西是强烈地偏向于Java语言的。它一点也不像是通用的 公平的 语言中立的VM/OS。<BR><BR>私下里，我会坚持使用可移植的C++作大部份工作，用不同的语言作余下的工作。<BR><FONT color=#ff0000>（"Java is not platform-independent, it is the platform"，B. S的这句评语对於C++用户有 很大的影响，译者在国外的几个新闻组里看到，有些C++高手甚至把这句话作为自己的签名档，以表明对Java的态度和誓死捍卫C++的决心。实际上有很多程序员不光是把自己喜爱的语言当成一种工具，更当成一种信仰。--译者）</FONT></P>
<H3><FONT color=#0000ff>11. 您怎麽看待C#语言？</FONT></H3>
<P>就C#语言本身我没什麽好说的。想让我相信这个世界还需要另外一个专有的语言可不是一件容易的事，而且这个语言还是专门针对某一个专有操作系统的，这就更让我难以接受。直截了当地说，我不是一个专有语言的痴迷者，而是一个开放的正式标准的拥护者。</P>
<H3><FONT color=#0000ff>12. 在做大项目时，您是不是真的推荐Ada，而不是C++？</FONT></H3>
<P>当然不是。我不知道这是谁传出来的谣言，肯定是一个Ada信徒，要麽是过份狂热，要麽是不怀好意。</P>
<H3><FONT color=#0000ff>13. 你愿不愿意将C++与别的语言比较？</FONT></H3>
<P>抱歉，我不愿意。你可以在The Design and Evolution of C++的介绍性文字里找到原因。<BR><BR>有不少书评家邀请我把C++与其它的语言相比，我已经决定不做此类事情。在此我想重申一个我很久以来一直强调的观点：语言之间的比较没什麽意义，更不公平。<FONT color=#8080ff>主流语言之间的合理比较要耗费很大的精力，多数人不会愿意付出这麽大的代价。另外还需要在广泛的应用领域有充份经验，保持一种不偏不倚 客观独立的立场，有 公正无私的信念。</FONT>我没时间，而且作为C++的创造者，在公正无私这一点上我永远不会获得完全的信任。<BR><BR>人们试图把各种语言拿来比较长短，有些现像我已经一次又一次地注意到，坦率地说我感到担 。作者们尽力表现的公正无私，但是最终都是无可救药地偏向于某一种特定的应用程序，某一种特定的编程风格，或者某一种特定的程序员文化。更糟的是，<FONT color=#8080ff>当某一种语言明显地比另一种语言更出名时，一些不易察觉的偷梁换柱就开始了：比较有名的语言中的缺陷被有意淡化，而且被拐弯抹角地加以掩饰；而同样的缺陷在不那麽出名的语言里就被描述为致命硬伤。类似的，有关比较出名的语言的技术资料经常更新，而不太出名的语言的技术资料往往是几年以前的，试问这种比较有何公正性和意义可言？所以我对於C++之外的语言的评论严格限制在一般性的特别特定的范畴里。</FONT><BR><BR>换言之，我认为C++是大多数人开发大部份应用程序时的最佳选择。<BR><FONT color=#0000ff></FONT></P>
<H3><FONT color=#0000ff>14. 别人可是经常拿他们的语言与C++比来比去，这让你感到不自在了吗？</FONT></H3>
<P>当这些比较不完整或者出於商业目的时，我确实感觉不爽。那些散布最广的比较性评论大多是由某种语言，比方说Z语言的拥护者发表的，其目的是为了证明Z比其它的语言好。由於C++被广泛地使用，所以C++通常成了黑名单上的头一个名字。通常，这类文章被夹在Z语言的供货商提供的产品之中，成了其市场竞争的一个手段。令人震惊的是，相当多的此类评论引用那些在开发Z语言的公司中工作的雇员的文章，而这些经不起考验文章无非是想证明Z是最好的。特别是在这些比较中确实有一些零零散散的事实，<FONT color=#ff0000>（所以更具欺骗性--译者）</FONT>，毕竟没有一种语言在任何情况下都是最好的。C++当然不完美，不过请注意，特意选择出来的事实虽然好像正确，但有时是完全的误导。<BR><BR><FONT color=#8080ff>以後再看到语言比较方面的文章时，请留心是谁写的，他的表述是不是以事实为依据，以公正为准绳，特别是评判的标准是不是对於所引述的每一种语言来说都公平合理。</FONT>这可不容易做到。</P>
<H3><FONT color=#0000ff>15. 在做小项目时，C优于C++吗？</FONT></H3>
<P>我认为非也。除了由於缺乏好的C++编译器而导致的问题之外，我从没有看到哪个项目用C会比用C++更合适。<FONT color=#ff0000>（不过现在C++编译器导致的问题还是不可忽略的，当你看到同样功能的C++程序可执行代码体积比C大一倍而且速度慢得多时，会对此有所感触的。--译者）</FONT></P>
<P><FONT color=#ff0000></FONT></P>
<H2>以下内容来自Visual C++ Developer's Journal主编<BR>Elden Nelson 2000年3月对B. S的专访<BR></H2>
<H3><FONT color=#0000ff>16.    如果您现在有机会从头设计C++语言，您会做些什麽不同的事情？</FONT></H3>
<P>当然，你永远都不可能重新设计一种语言，那没有意义，而且任何一种语言都是它那个时代的产物。如果让我今天再设计一种语言，我仍然会综合考虑逻辑的优美 效率 通用性 实现的复杂程度和人们的喜好。要知道人们的习惯对於他们的喜好有 巨大的影响。<BR><BR>现在，我会寻找一种简单得多的语法，我会把类型系统的冲突问题限制在很少的几种情况里，而且你能很容易的发现这些问题。这样就能够很容易的禁止不安全的操作。<FONT color=#ff0000>（B. S的原则是：对於糟糕的代码，就算是不能完全禁止，至少也要让它大白于天下，而不是藏在阴暗的角落里暗箭伤人。C++实际上已经提供了这样的机制，例如如果你使用象reinterpret_cast<int>(pointer)这样的很明显是非常糟糕的表达式进行造型，别人会很容易地找到问题所在。只不过C++仍然允许你使用传统的 C风格的造型机制，而又有不少人一直使用这种老式的风格，所以才引来麻烦多多。B. S的意思是说，要是现在能够禁止老式的风格该有多好 作为语言设计者的他，恐怕是没有这个机会了，但是作为语言使用者的我们，却还有很大的希望去改进自己的代码。何去何从，应该是我们深思的时候了。--译者）</FONT><BR><BR>我还会把核心语言的体积尽可能搞得小一些，包括类和模板的关键的抽象特性，而把很多其它的语言特性放在库里来解决。当然我也会保证核心语言足够的强大，使得那些库本身也足以用这个核心语言来产生。我可不希望标准库的创建需要用到什麽不属於该语言本身的神秘机制。另外我会让这个核心语言的定义更加精确。<FONT color=#ff0000>（有不少新的语言在建库时就使用了一些"不属於该语言本身的神秘机制"，比如VB和JAVA。从理论上讲，这是近乎无赖的行径，所以B. S不以为然。不过从实用出发倒也无伤大雅。--译者）</FONT><BR><BR>最重要的是，我会在该语言被广泛使用之前尽可能维持一个很长的酝酿期，这样我可以以其他人的反馈为基础进行改进。这可能是最困难的，因为<FONT color=#8080ff>一旦有什麽东西是明显出色和有前途的，大家就会蜂拥而至的来使用它，此後作任何不兼容的修正都会是非常困难的</FONT>。<BR><BR>我相信这些思想与我当初设计C++时的理念是非常类似的，同样也是这些思想指引 一二十年来C++的不断演化。当然，我认为现在还没有什麽东西能让我觉得像是"完美的语言"。<BR></P>
<H3><FONT color=#0000ff>17. 您预期C++做哪些增强，会不会删掉一些东西？</FONT></H3>
<P>很不幸，虽然有一些东西很应该扔掉，但恐怕很难真的删掉任何东西。<FONT color=#8080ff>第一个应该抛弃的东西就是C风格的造型机制和类型截断转换。就算不禁止，编译器的作者们至少也应该对这种行为给与强烈的警告。</FONT>我希望能用类似vector的东西彻底取代数组，但这显然是不可能的。不过如果程序员们能主动使用vector来代替数组，就会立刻受益匪浅。关键是你不必再使用C++中最复杂难缠的技巧了，现在有优秀得多的替代方案。<BR><BR>至於主要的特性，<FONT color=#8080ff>我没想去掉任何东西</FONT>。特别是那些把C++与C区别开来的主要特性恐怕没法风平浪静的被抛掉。<FONT color=#8080ff>通常问这些问题的人是希望我挑出诸如多继承 异常 模板等机制来接受批判。</FONT>所以在这我想大声讲清楚，我认为<FONT color=#8080ff>多继承机制对於静态类型语言实现继承性来说是必需的，异常机制是在大系统中对付错误的正确方法，模板机制是进行类型安全的 精致的和高效的程序设计的灵丹妙药</FONT>。我们可以在小的细节上对於这些机制挑挑刺，但在大的方面，这些基本的概念都必须坚持。<BR>现在我们仍在学习标准C++，也正在标准所提供的特性基础上发展出更新的 更有趣的编程技术。特别是人们刚刚开始使用STL和异常机制，还有很多高效强大的技术鲜为人知，所以大可不必急匆匆的跑去增加什麽新的机制。<BR><BR>我认为当前的重点是提供很多新的 比以前更加精致的 更有用的库，这方面潜力巨大。例如，如果有一个能被广泛使用的 更精致的支持<FONT color=#ff00ff>并发程序设计</FONT>的库，那将是一大福音--C风格的线程库<FONT color=#ff0000>（例如Pthread--译者）</FONT>实在不够好。我们也就可以与各种其他的系统，例如SQL以及不同的组件模型更好地契合起来。数值计算领域的人们在这方面好像已经走在了前面，类似像Blitz++ POOMA MTL之类的高效而精致的库的开发已经取得了非凡的成就。<FONT color=#ff0000>（译者在Internet上造访了Blitz++和POOMA的主页，前者是一个高性能数学库，据称其性能与Fortran 77不相上下，同时又支持大量的C++特性。我想凡是对於数值计算领域有所了解的人都知道这有多麽伟大的意义。POOMA则是一个专门研究C++并行数学算法的项目，它的前景更加不可限量。译者非常认同B. S的这个观念。--译者）</FONT><BR><BR>有了足够的经验之後，我们就能更好的决定应该对标准做些什麽调整。</P>
<H3><FONT color=#0000ff>18. 显然，这几年世界变了，正在走向一个以Web为中心 分布式计算为主流的时代。那麽您觉得C++还能维持其地位吗？程序员们可不可能把若干种专用语言（比如Perl Javascript）综合运用以彻底取代某一种通用语言？</FONT><FONT color=#ff0000>（C++就是这样的通用语言--译者）</FONT><FONT color=#0000ff>为了配合新的计算模式，C++及其标准库应该做怎样的调整？</FONT></H3>
<P>从来没有哪一种语言能适合所有的工作，我恐怕以後也不会有。实际系统通常是用多种语言和工具构造起来的。C++只是想成为若干语言和工具中的一个，当某些专用语言在其领域里特别突出时，它们可以与C++互为补充。也就是说，我觉得如果大多数现在的专用语言能借助特定领域的C++库共同工作的话，它们会表现得更出色。脚本语言通常导致难以维护的代码，而且也没有给程序的结构 可扩展性和可维护性的优化留下什麽余地。<BR><BR>我不敢肯定未来的代码是否真的会是以Web为中心的。就算是直接处理Web的系统也主要是由处理本地资源，如IP连接之类的程序模块构成的。<BR><BR>地理上的分布性以及服务器软件对於并发机制的高度依赖对於系统的建造者来说的确是个挑战。有些针对上述问题的库已经出现，也许我们将会看到它们最终得以标准化。当然，一些原操作和保证规则应该被加到核心语言中以提供对这些库的更佳支持。<BR><BR>总的来说，对於Web和网络，我们非常需要一个真正的系统/网络级的安全模型。指望JavaScript之类的脚本语言实现这个模型无异于白日做梦。<BR><BR>注意，我也没说C++提供了这个问题的解决方式。C++的重心是高效的访问系统资源，而不是反欺诈。</P>
<H3><FONT color=#0000ff>19. 您看C++未来的走向如何？在接下来的10年里它会衰落吗？或者是基本保持现在的形式？或者发展变化呈不同的形式？</FONT></H3>
<P>C++有 最美好的未来。用它你能写出伟大的代码。除了故意进行恶意欺诈，C++仍将是开发高性能 高复杂度系统的最好语言。据我所知，没有那种语言能在通用性 效率和精致三方面的统一上可与C++相题并论。<BR><BR>我没看到C++有衰落的徵兆。在我能预见的未来里，它的用途还会不断增长。当然，在未来的十年里我们会看到一些变化，但不会像你想得那麽显着。跟每一种语言一样，C++也会发展变化。<FONT color=#8080ff>"语言专家们"要求改进的喧嚣声震耳欲聋，但是系统开发者们的基本请求是保持稳定。</FONT><BR><BR>C++会改进，但是这些改进将主要是为了反映从实践中得来的经验教训，而不会是为了追风尚赶时髦。为了更高效地使用一些新的编程技术，比如通用编程技术，可能会增加一些小的特性。会有大量的库涌现，<FONT color=#ff00ff>我预期会出现一种崭新的 更出色的库支持机制</FONT>。我希望新的扩展主要集中在支持抽象方面的一般特性，而不是为支持某些特殊任务的特定机制。<BR><BR>例如，"属性"这个概念是很有用的，但我不认为在一种通用编程语言中有它的容身之地。用标准C++的一组类可以很容易地支持这一概念。如果我们感觉那族类对於"属性"这一概念的支持不尽如人意，也不会立刻跑去在语言里增加属性机制，而是仔细考虑如何改进类和模板以帮助库设计人员尽可能接近"属性"这个概念。也许通过改进函数对象的机制能够给这个问题一个满意的答复。<BR><BR>为了使C++在接下来的十几年中保持灵活可变，很基本的一点就是不要让标准C++赶什麽学术或者商业的时髦。人们要求增加的特性中很大一部份通过使用现有的标准C++开发新库的方式都可以实现。还有，事实上人们渴望得到的很多特性已经被包括在标准C++中，并且被最新的编译器支持。<BR><BR><FONT color=#8080ff>对许多程序员来说，提高代码质量的最佳途径不是追求什麽语言扩展，而是好好地 慢慢地品味最新的C++技术书籍</FONT><FONT color=#ff0000>（可惜我们到目前为止连这种机会都没有--译者）。</FONT></P>
<H3><FONT color=#0000ff>20. 您怎麽看待脚本语言的兴旺态势？特别是Python，似乎提供了一种学习OO技术的更简单的途径</FONT></H3>
<P>有些语言很不错。比如Python，我很喜欢。但是我认为你从不同的语言中学到的OO技术是不完全相同的。当然，每一个专业的程序员都需要通晓几门语言，并且了解各种语言在编程和设计技术上的不同。<BR><BR>在我看来，用脚本语言建造的系统与用C++那样的通用语言建造的系统大不相同。从两类语言中学到的技术区别明显。在OO技术里也不存在什麽通用部份对於各种系统的高效建造来说都是至关重要的。</P>
<H3><FONT color=#0000ff>21. 有没有计划往标准C++里增加一些新的特性以支持分布式计算？</FONT></H3>
<P>没有，我也不认为有这个必要。用更好的库就差不多能解决问题了。最多，为了支持这类的库，我们可能会增加一些低级的原操作和规则<BR></P>
<H3><FONT color=#0000ff>22. 未来C++有没有可能定一个可移植的二进制接口？</FONT></H3>
<P>如果你说的"可移植"是指跨硬件和块操作系统的可移植，我想回答是不会。我们当然可以设计一个解释器或者虚拟机<FONT color=#ff0000>（如同Java的做法--译者）</FONT>，但这样一来，由於无法以最优的方式访问系统资源，C++的能力就会受到削弱，。我真正希望在不远的将来能够看见的东西是<FONT color=#8080ff>platform ABIs</FONT>（ABI， Application Binary Interface） 。例如，<FONT color=#8080ff>有人正在努力为Intel新的IA64体系定义C++ ABI，我想这些努力会得到用户们的巨大支持。能够把不同编译器产生的代码编译在一起将会是一项十分有意义的事情。</FONT></P>
<H3><FONT color=#0000ff>23. 在不少流行领域，C++正在渐渐失去光芒，因为它要求人们花很大的精力去对付一些很基本的工作，比如管理内存（因为没有垃圾收集机制），管理模块之间的依赖性（因为没有包机制），管理组件的版本。C++缺乏一些现代语言已经视为标准的特性。比如传言中最酷的Java语言就特别重视这些问题。那麽在解决这些问题是否会导致C++的发展背离其根本宗旨呢？C++应该怎样发展以保证我们在这种语言上的投资能有合理的回报，而不是被迫去重新使用另一种语言？</FONT></H3>
<P>我倒还没有注意到C++比以前用的少了。相反，我看到的指标表明C+的使用还在稳定地增长 。<FONT color=#8080ff>只不过这种基数很大的稳定增长以及在标准性 移植性和库方面的不断提高并没有造成什麽具有欺骗性的新闻效应而已</FONT>。我认为你所说的"失去光芒"只不过是市场推销和新闻意义上的现象。<BR><BR>如果你需要垃圾收集机制的话，你可以在C++应用程序中插入一个垃圾收集器。有不少自由的和商业的垃圾收集器已经在重要的实践中被证明是很出色的。<BR><BR><FONT color=#8080ff>如果你不想使用垃圾收集机制，也没关系。你可以使用标准容器类，它们大大减少了对於显式分配和回收内存的需要</FONT>。这样，使用现代的库和现代的编程风格，你能够避免大部份的内存管理问题。<BR><BR>同样的技术还能够用来避免一般资源的管理问题。并不是只有内存才会泄漏，线程句柄 文件 互斥锁 网络连接等都是重要的资源，为了建立可靠的系统，这些资源必须被正确的管理。<FONT color=#8080ff>如果你觉得有了垃圾收集机制就可以解决所有的资源管理问题，那麽你最好赶快从美梦中醒来。</FONT><BR><BR>C++提供了很多机制来管理一般性的资源。关键的手段--"Resource Acquisition is Initialization"<FONT color=#ff0000>(这是著名的RAII惯用法，阅读原文时会经常遇到，其意义是说将所有的资源分配申请放在对象初始化过程中进行，而将资源释放动作放在对象销毁过程中——译者)</FONT>可以使用函数对象来管理生存期问题。语言中关於对象的局部构造和异常机制对这项技术提供了支持。<BR><BR>某些语言的狂热支持者总是用讽刺漫画的笔法描述C++，然而C++实际上要好得多。特别是我觉得很多其他的特性已经泛滥不堪了，在C++中，通常这些特性能够很容易的被模拟出来。相反的，新的语言在推广的过程中总是不断地增加新的特性，<FONT color=#8080ff>这就是为什麽从一种语言诞生到被广泛使用，其体积通常会增加个两三倍。</FONT><BR><BR>目前，最为个人和组织，对於C++的最好投资就是去更好地理解标准C++和现代的C++设计编程技术。大多数人使用C++的方式实际上停留80年代中期的水平，甚至比那更陈旧。<BR><BR>至於模块依赖性问题，我的观点是，在编程语言的工作和系统的工作之间应该有一个明显的界线，依赖关系应该尽可能地与编程语言分开，而由系统来支持。<BR><BR>我不认为组建版本的问题应该由编程语言来解决，这是一个系统范畴里的问题，在语言里应该通过提供相应的库来解决。C++有这样的机制。<BR><BR>解决这样的问题不会使C++偏离轨道。但是给C++增加很多特殊的特性就会使C++偏离轨道，而且在保持可移植性和平台独立性方面也会是一个倒退。<BR></P>
<H3><FONT color=#0000ff>24. 标准C++推出有段时间了，Java也大踏步地往前走而且取得了显着的进步，您现在怎麽比较Java与C++？您觉得Java想要变成像C++一样"好"的语言还需要做些什麽？您举的C++从Java身上学到了什麽经验吗？有没有什麽Java的特性您认为是可以被C++吸纳的？</FONT></H3>
<P>我不比较语言。做好这项工作是十分困难的，而且很少具有专业水准。<BR><BR>我认为C++的进步会是主要以它的用户在使用中遇到的问题以及其自身逻辑为基础。当然，其他语言中的某些思想也会被考虑，但不能被简单的移花接木过来。你必须审视那些机制在技术上和思想上的背景，并且找到在C++中支持这些技术的最佳方案。<BR><BR>有时最好的选择是综合使用几种语言。毕竟没有任何一种语言是放之四海而皆优的。C++现在是，将来也继续会是在广泛应用领域中最好的语言之一。但是，我们不能被拉下水，不能把所有可能的特性都加到C++里面来向大众献媚。我认为Java和C++现在和将来都会是十分不同的语言，语法相似，但背後的对象模型明显不同。<BR><BR><FONT color=#8080ff>对於我来说，一个很重要的区别是C++有一个ISO标准，而Java则是一个专有语言。</FONT></P>
<H3><FONT color=#0000ff>25. 在Java刚刚出现的那几年，有很多欺骗性的言论说它将会是终极语言，会取代C++。您觉得在过去两三年里Java对C++的追随者们有什麽影响？</FONT></H3>
<P>到现在关於Java的不实之辞也还随处可见。暂且不提Java在过去5年间的创纪录的发展，狂热的大众似乎认为Java将最终取代的不仅仅是C++，而且还有所有其他的编程语言。但在另一方面，C++的使用仍在继续增长。我不认为Java对於C++的影响已经使得人们转而把本来打算用来创造更好的C++工具库的资源调过去开发Java工具库。Java对於学习编程的人来说没有太多的新东西，所以对於C++的定义也没什麽影响。在那个领域，Java还得努力追赶。例如，<FONT color=#8080ff>我认为为Sun迟早会往Java里加入类似模板的机制</FONT>。</P>
<P>人们应该认识到C++和Java的目标是何等的不同。 以C++的设计理念来衡量Java，或是以Java的设计理念来衡量C++，得出的结论都不会很好。<BR><BR>在访谈即将结束时，或许我该再次表明态度：C++仍然是我喜爱的语言，在写代码时你会发现没有那种语言能像它那样在如此广泛的应用领域和平台上同时达成如此的高效与精致。<BR></P><![CDATA[<FONT color=#ff0000>[译者按] Bjarne Stroustrup博士，1950年出生于丹麦，先後毕业于丹麦阿鲁斯大学和英国剑挢大学，AT&T大规模程序设计研究部门负责人，AT&T 贝尔实验室和ACM成员。1979年，B. S开始开发一种语言，当时称为"C with Class"，後来演化为C++。1998年，ANSI/ISO C++标准建立，同年，B. S推出其经典着作The C++ Programming Language的第三版。C++的标准化标志 B. S博士倾20年心血的伟大构想终於实现。但是，计算技术的发展一日千里，就在几年前人们还猜想C++最终将一统天下，然而随 Internet的爆炸性增长，类似Java C#等新的 现代感十足的语言咄咄逼人，各种Script语言更是如雨後春笋纷纷涌现。在这种情况下，人们不禁有些惶恐不安。C++是不是已经过时了呢？其前景如何？标准C++有怎样的意义？应该如何学习？我们不妨看看B. S对这些问题的思考。以下文字是译者从Stroustrup1998年之後发表的若干文章 谈话笔记中精选出来的，由於出处不一，内容多有重复，为保持完整，亦一并译出。</FONT>
<H2>以下内容选自B. S在自己主页上发表的FAQ</H2>
<H3><FONT color=#0000ff>1. 请谈谈C++书。</FONT></H3>
<P>没有，也不可能有一本书对於所有人来说都是最好的。不过对於那些真正的程序员来说，如果他喜欢从"经典风格"的书中间学习一些新的概念和技术，我推荐我的The C++ Programming Language, 1998年的第三版和特别版。那本书讲的是纯而又纯的C++，完全独立於平台和库（当然得讲到标准库）。该书面向那些有一定经验的程序员，帮助他们掌握C++，但不适合毫无经验的初学者入门，也不适合那些临时程序员品尝C++快餐。所以这本书的重点在於概念和技术，而且在完整性和精确性上下了不少功夫。如果你想知道为什麽C++会变成今天的模样，我的另一本书 The Design and Evolution of C++ 能给你满意的答案。理解设计的原则和限制能帮助你写出更好的程序。www.accu.org是最好的书评网站之一，很多有经验的程序员在此仗义执言，不妨去看看。</P>
<H3><FONT color=#0000ff>2. 学习C++要花多长时间？</FONT></H3>
<P>这要看你说的"学习"是什麽意思了。如果你是一个Pascal程序员，你应该能很快地使你的C++水平达到与Pascal相近的程度；而如果你是一个C程序员，一天之内你就能学会使用C++进行更出色的C风格编程。另一方面，如果你想完全掌握C++的主要机制，例如数据抽象，面向对象编程，通用编程，面向对象设计等等，而此前又对这些东西不很熟悉的话，花上个一两年是不足为奇的。那麽是不是说这就是学习C++所需要的时间呢？也许再翻一番，我想打算成为更出色的设计师和程序员最起码也要这麽长的时间。<FONT color=#8080ff>如果学习一种新的语言不能使我们的工作和思想方式发生深刻<BR>的变革，那又何苦来哉？跟成为一个钢琴家或者熟练掌握一门外语相比，学习一种新的 不同的语言和编程风格还算是简单的。</FONT></P>
<H3><FONT color=#0000ff>3. 了解C是学习C++的先决条件吗？</FONT></H3>
<P>否 C++中与C相近的子集其实比C语言本身要好学，类型方面的错误会少一些，也不像C那样绕圈子，还有更好的支持库。所以应该从这个子集开始学习C++。<BR></P>
<H3><FONT color=#0000ff>4. 要想成为真正的OO程序员，我是不是得先学习Smalltalk？</FONT></H3>
<P>否。如果你想学Smalltaok，尽管去学。这种语言很有趣，而且学习新东西总是一个好主意。但是Smalltalk不是C++，而且把Smalltalk的编程风格用在C++里不会有什麽好结果。如果你想成为一个出色的C++程序员，而且也没有几个月的时间百无聊赖，请你集中力量学好C++以及其背後的思想。<BR></P>
<H3><FONT color=#0000ff>5. 我如何开始学习C++？</FONT></H3>
<P>这取决于你的基础和学习动机。如果你是个初学者，我想你最好找个有经验的程序员来帮助你，要不然你在学习和实践中不可避免的犯下的种种错误会大大地打击你的积极性。另外，即使你的编译器配备了充足的文档资料，一本C++书籍也永远是必不可少的，毕竟文档资料不是学习编程思想的好教材。<BR><BR><FONT color=#8080ff>选择书籍时，务必注意该书是不是从一开始就讲授标准C++</FONT>，<FONT color=#8080ff>并且矢志不渝地使用标准库机制。</FONT>例如，从输入中读取一个字符串应该是这样的：<BR><BR>    string s;    // Standard C++ style<BR>    cin >> s;<BR><BR>而不是这样的：<BR><BR>    char s[MAX];    /* Standard C style */<BR>    scanf("%s",s);<BR><BR>去看看那些扎实的C++程序员们推荐的书吧。记住，没有哪本书对所有人来说都是最好的。另外，要写地道的C++程序，而避免用C++的语法写传统风格的程序，新瓶装旧酒没多大意义。<FONT color=#ff0000>（遗憾的是，目前在市面上的中文C++教材中，符合B. S的这个标准的可以说一本都没有，大家只好到网上找一些英文的资料来学习了。--译者）</FONT><BR></P>
<H3><FONT color=#0000ff>6. 怎样改进我的C++程序？</FONT></H3>
<P>不好说。这取决于你是怎麽使用该语言的。<FONT color=#8080ff>大多数人低估了抽象类和模板的价值，反过来却肆无忌惮地使用造型机制(cast)和宏</FONT>。这方面可以看看我的文章和书。抽象类和和模板的作用当然是提供一种方便的手段建构单根的类层次或者重用函数，但更重要的是，它们作为接口提供了简洁的 逻辑性的服务表示机制。<BR></P>
<H3><FONT color=#0000ff>7. 语言的选择是不是很重要？</FONT></H3>
<P><FONT color=#8080ff>是，但也别指望奇迹</FONT>。很多人似乎相信某一种语言能够解决他们在系统开发中遇到的几乎所有问题，他们不断地去寻找完美的编程语言，然後一次次的失败，一次次的沮丧。另外一些人则将编程语言贬为无关紧要的细节，把大把大把的银子放在开发流程和设计方法上，他们永远都在用COBOL C和一些专有语言。一种优秀的语言，例如C++，能帮助设计者和程序员做很多事情，而其能力和缺陷又能够被清楚地了解和对待。</P>
<H3><FONT color=#0000ff>8. ANSI/ISO标准委员会是不是糟蹋了C++？</FONT></H3>
<P>当然不是 他们（我们）的工作很出色。你可以在一些细节上找些歪理来挑刺，但我个人对於这种语言以及新的标准库可是欣欣然。ISO C++较之C++的以前版本更出色更有条理。相对於标准化过程刚刚开始之初，你今天可以写出更优雅 更易于维护的C++程序。新的标准库也是一份真正的大礼。由於标准库提供了strings, lists, vectors, maps以及作用于其上的基本算法，使用C++的方式已经发生了巨大的变化。<BR></P>
<H3><FONT color=#0000ff>9. 你现在有没有想删除一些C++特性？</FONT></H3>
<P>没有，真的。<FONT color=#8080ff>问这些问题的人大概是希望我回答下面特性中的一个：多继承 异常 模板和RTTI</FONT>。但是没有它们，C++就是不完整的。在过去的N年中，我已经反复考虑过它们的设计，并且与标准委员会一起改进了其细节，但是没有一个能被去掉又不引起大地震。<BR><BR>从语言设计的角度讲，我最不喜欢的部份是与C兼容的那个子集，但又不能把它去掉，因为那样对於在现实世界里工作的程序员们来说伤害太大了。C++与C兼容，这是一项关键的设计决策，绝对不是一个叫卖的噱头。<FONT color=#8080ff>兼容性的实现和维护是十分困难的，但确实使程序员们至今受益良多。</FONT><BR><BR>但是现在，C++已经有了新的特性，程序员们可以从麻烦多多的C风格中解脱出来。例如，使用标准库里的容器类，象vector, list, map, string等等，可以避免与底层的指针操作技巧混战不休。<BR></P>
<H3><FONT color=#0000ff>10. 如果不必和C兼容，你所创造的语言是不是就会是Java?</FONT></H3>
<P>不是，差得远。如果人们非要拿C++和Java来作比较，我建议他们去阅读The Design and Evolution of C++，看看C++为什麽是今天这个样子，用我在设计C++时遵从的原则来检验这两种语言。这些原则与SUN的Java开发小组所持的理念显然是不同的。除了表面语法的相似性之外，C++与Java是截然不同的语言。在很多方面，Java更像Smalltalk<FONT color=#ff0000>（译者按：我学习Java时用的是Sun的培训教材，里面清楚地写道：Java在设计上采用了与C++相似的语法，与Smalltalk相似的语义。所以可以说Java与C++是貌合神离，与Smalltalk才是心有灵犀）</FONT>。Java语言相对简单，这部分是一种错觉，部份是因为这种语言还不完整。<FONT color=#8080ff>随 时间的推移，Java在体积和复杂程度上都会大大增长。在体积上它会增长两到三倍，而且会出现一些实现相关的扩展或者库。这是一条每个成功的商业语言都必须走过的发展之路。</FONT>随便分析一种你认为在很大范围内取得了成功的语言，我知道肯定是无有例外者，而且实际上这非常有道理。<BR><BR>上边这段话是在Java 1.1推出之前写的。<FONT color=#8080ff>我确信Java需要类似模板的机制，并且需要增强对於固有类型的支持。</FONT>简单地说，就是为了基本的完整性也应该做这些工作。另外还需要做很多小的改动，大部份是扩展。1998年秋，我从James Gosling<FONT color=#ff0000>（Java语言的创始人--译者）</FONT>那里得到一份建议书，说是要在Java中增加固有类型 操作符重载以及数学计算支持。还有一篇论文，是数学分析领域的世界级大师，伯克利大学的W. Kahan教授所写的How Java's Floating-Point Hurts Everyone Everywhere<FONT color=#ff0000>（"且看Java的浮点运算如何危害了普天下的芸芸众生"--译者）</FONT>，揭露了Java的一些秘密。<BR><BR>我发现在电视和出版物中关於Java的鼓吹是不准确的，而且气势汹汹，让人讨厌。大肆叫嚣凡是非Java的代码都是垃圾，这是对程序员的侮辱；建议把所有的保留代码都用Java重写，这是丧心病狂，既不现实也不负责任。Sun和他的追随者似乎觉得为了对付微软罪恶的"帝国时代"，就必须如此自吹自擂。但是侮辱和欺诈只会把那些喜欢使用不同编程语言的程序员逼到微软阵营里去。<BR><BR><FONT color=#8080ff>Java并非平台无关，它本身就是平台</FONT>。跟Windows一样，它也是一个专有的商业平台。也就是说，你可以为Windows/Intel编写代码，也可以为Java/JVM编写代码，<FONT color=#8080ff>在任何一种情况下，你都是在为一个属於某个公司的平台写代码，这些代码都是与该公司的商业利益扯在一起的。</FONT>当然你可以使用任何一种语言，结合操作系统的机制来编写可供JVM执行的程序，但是JVM之类的东西是强烈地偏向于Java语言的。它一点也不像是通用的 公平的 语言中立的VM/OS。<BR><BR>私下里，我会坚持使用可移植的C++作大部份工作，用不同的语言作余下的工作。<BR><FONT color=#ff0000>（"Java is not platform-independent, it is the platform"，B. S的这句评语对於C++用户有 很大的影响，译者在国外的几个新闻组里看到，有些C++高手甚至把这句话作为自己的签名档，以表明对Java的态度和誓死捍卫C++的决心。实际上有很多程序员不光是把自己喜爱的语言当成一种工具，更当成一种信仰。--译者）</FONT></P>
<H3><FONT color=#0000ff>11. 您怎麽看待C#语言？</FONT></H3>
<P>就C#语言本身我没什麽好说的。想让我相信这个世界还需要另外一个专有的语言可不是一件容易的事，而且这个语言还是专门针对某一个专有操作系统的，这就更让我难以接受。直截了当地说，我不是一个专有语言的痴迷者，而是一个开放的正式标准的拥护者。</P>
<H3><FONT color=#0000ff>12. 在做大项目时，您是不是真的推荐Ada，而不是C++？</FONT></H3>
<P>当然不是。我不知道这是谁传出来的谣言，肯定是一个Ada信徒，要麽是过份狂热，要麽是不怀好意。</P>
<H3><FONT color=#0000ff>13. 你愿不愿意将C++与别的语言比较？</FONT></H3>
<P>抱歉，我不愿意。你可以在The Design and Evolution of C++的介绍性文字里找到原因。<BR><BR>有不少书评家邀请我把C++与其它的语言相比，我已经决定不做此类事情。在此我想重申一个我很久以来一直强调的观点：语言之间的比较没什麽意义，更不公平。<FONT color=#8080ff>主流语言之间的合理比较要耗费很大的精力，多数人不会愿意付出这麽大的代价。另外还需要在广泛的应用领域有充份经验，保持一种不偏不倚 客观独立的立场，有 公正无私的信念。</FONT>我没时间，而且作为C++的创造者，在公正无私这一点上我永远不会获得完全的信任。<BR><BR>人们试图把各种语言拿来比较长短，有些现像我已经一次又一次地注意到，坦率地说我感到担 。作者们尽力表现的公正无私，但是最终都是无可救药地偏向于某一种特定的应用程序，某一种特定的编程风格，或者某一种特定的程序员文化。更糟的是，<FONT color=#8080ff>当某一种语言明显地比另一种语言更出名时，一些不易察觉的偷梁换柱就开始了：比较有名的语言中的缺陷被有意淡化，而且被拐弯抹角地加以掩饰；而同样的缺陷在不那麽出名的语言里就被描述为致命硬伤。类似的，有关比较出名的语言的技术资料经常更新，而不太出名的语言的技术资料往往是几年以前的，试问这种比较有何公正性和意义可言？所以我对於C++之外的语言的评论严格限制在一般性的特别特定的范畴里。</FONT><BR><BR>换言之，我认为C++是大多数人开发大部份应用程序时的最佳选择。<BR><FONT color=#0000ff></FONT></P>
<H3><FONT color=#0000ff>14. 别人可是经常拿他们的语言与C++比来比去，这让你感到不自在了吗？</FONT></H3>
<P>当这些比较不完整或者出於商业目的时，我确实感觉不爽。那些散布最广的比较性评论大多是由某种语言，比方说Z语言的拥护者发表的，其目的是为了证明Z比其它的语言好。由於C++被广泛地使用，所以C++通常成了黑名单上的头一个名字。通常，这类文章被夹在Z语言的供货商提供的产品之中，成了其市场竞争的一个手段。令人震惊的是，相当多的此类评论引用那些在开发Z语言的公司中工作的雇员的文章，而这些经不起考验文章无非是想证明Z是最好的。特别是在这些比较中确实有一些零零散散的事实，<FONT color=#ff0000>（所以更具欺骗性--译者）</FONT>，毕竟没有一种语言在任何情况下都是最好的。C++当然不完美，不过请注意，特意选择出来的事实虽然好像正确，但有时是完全的误导。<BR><BR><FONT color=#8080ff>以後再看到语言比较方面的文章时，请留心是谁写的，他的表述是不是以事实为依据，以公正为准绳，特别是评判的标准是不是对於所引述的每一种语言来说都公平合理。</FONT>这可不容易做到。</P>
<H3><FONT color=#0000ff>15. 在做小项目时，C优于C++吗？</FONT></H3>
<P>我认为非也。除了由於缺乏好的C++编译器而导致的问题之外，我从没有看到哪个项目用C会比用C++更合适。<FONT color=#ff0000>（不过现在C++编译器导致的问题还是不可忽略的，当你看到同样功能的C++程序可执行代码体积比C大一倍而且速度慢得多时，会对此有所感触的。--译者）</FONT></P>
<P><FONT color=#ff0000></FONT></P>
<H2>以下内容来自Visual C++ Developer's Journal主编<BR>Elden Nelson 2000年3月对B. S的专访<BR></H2>
<H3><FONT color=#0000ff>16.    如果您现在有机会从头设计C++语言，您会做些什麽不同的事情？</FONT></H3>
<P>当然，你永远都不可能重新设计一种语言，那没有意义，而且任何一种语言都是它那个时代的产物。如果让我今天再设计一种语言，我仍然会综合考虑逻辑的优美 效率 通用性 实现的复杂程度和人们的喜好。要知道人们的习惯对於他们的喜好有 巨大的影响。<BR><BR>现在，我会寻找一种简单得多的语法，我会把类型系统的冲突问题限制在很少的几种情况里，而且你能很容易的发现这些问题。这样就能够很容易的禁止不安全的操作。<FONT color=#ff0000>（B. S的原则是：对於糟糕的代码，就算是不能完全禁止，至少也要让它大白于天下，而不是藏在阴暗的角落里暗箭伤人。C++实际上已经提供了这样的机制，例如如果你使用象reinterpret_cast<int>(pointer)这样的很明显是非常糟糕的表达式进行造型，别人会很容易地找到问题所在。只不过C++仍然允许你使用传统的 C风格的造型机制，而又有不少人一直使用这种老式的风格，所以才引来麻烦多多。B. S的意思是说，要是现在能够禁止老式的风格该有多好 作为语言设计者的他，恐怕是没有这个机会了，但是作为语言使用者的我们，却还有很大的希望去改进自己的代码。何去何从，应该是我们深思的时候了。--译者）</FONT><BR><BR>我还会把核心语言的体积尽可能搞得小一些，包括类和模板的关键的抽象特性，而把很多其它的语言特性放在库里来解决。当然我也会保证核心语言足够的强大，使得那些库本身也足以用这个核心语言来产生。我可不希望标准库的创建需要用到什麽不属於该语言本身的神秘机制。另外我会让这个核心语言的定义更加精确。<FONT color=#ff0000>（有不少新的语言在建库时就使用了一些"不属於该语言本身的神秘机制"，比如VB和JAVA。从理论上讲，这是近乎无赖的行径，所以B. S不以为然。不过从实用出发倒也无伤大雅。--译者）</FONT><BR><BR>最重要的是，我会在该语言被广泛使用之前尽可能维持一个很长的酝酿期，这样我可以以其他人的反馈为基础进行改进。这可能是最困难的，因为<FONT color=#8080ff>一旦有什麽东西是明显出色和有前途的，大家就会蜂拥而至的来使用它，此後作任何不兼容的修正都会是非常困难的</FONT>。<BR><BR>我相信这些思想与我当初设计C++时的理念是非常类似的，同样也是这些思想指引 一二十年来C++的不断演化。当然，我认为现在还没有什麽东西能让我觉得像是"完美的语言"。<BR></P>
<H3><FONT color=#0000ff>17. 您预期C++做哪些增强，会不会删掉一些东西？</FONT></H3>
<P>很不幸，虽然有一些东西很应该扔掉，但恐怕很难真的删掉任何东西。<FONT color=#8080ff>第一个应该抛弃的东西就是C风格的造型机制和类型截断转换。就算不禁止，编译器的作者们至少也应该对这种行为给与强烈的警告。</FONT>我希望能用类似vector的东西彻底取代数组，但这显然是不可能的。不过如果程序员们能主动使用vector来代替数组，就会立刻受益匪浅。关键是你不必再使用C++中最复杂难缠的技巧了，现在有优秀得多的替代方案。<BR><BR>至於主要的特性，<FONT color=#8080ff>我没想去掉任何东西</FONT>。特别是那些把C++与C区别开来的主要特性恐怕没法风平浪静的被抛掉。<FONT color=#8080ff>通常问这些问题的人是希望我挑出诸如多继承 异常 模板等机制来接受批判。</FONT>所以在这我想大声讲清楚，我认为<FONT color=#8080ff>多继承机制对於静态类型语言实现继承性来说是必需的，异常机制是在大系统中对付错误的正确方法，模板机制是进行类型安全的 精致的和高效的程序设计的灵丹妙药</FONT>。我们可以在小的细节上对於这些机制挑挑刺，但在大的方面，这些基本的概念都必须坚持。<BR>现在我们仍在学习标准C++，也正在标准所提供的特性基础上发展出更新的 更有趣的编程技术。特别是人们刚刚开始使用STL和异常机制，还有很多高效强大的技术鲜为人知，所以大可不必急匆匆的跑去增加什麽新的机制。<BR><BR>我认为当前的重点是提供很多新的 比以前更加精致的 更有用的库，这方面潜力巨大。例如，如果有一个能被广泛使用的 更精致的支持<FONT color=#ff00ff>并发程序设计</FONT>的库，那将是一大福音--C风格的线程库<FONT color=#ff0000>（例如Pthread--译者）</FONT>实在不够好。我们也就可以与各种其他的系统，例如SQL以及不同的组件模型更好地契合起来。数值计算领域的人们在这方面好像已经走在了前面，类似像Blitz++ POOMA MTL之类的高效而精致的库的开发已经取得了非凡的成就。<FONT color=#ff0000>（译者在Internet上造访了Blitz++和POOMA的主页，前者是一个高性能数学库，据称其性能与Fortran 77不相上下，同时又支持大量的C++特性。我想凡是对於数值计算领域有所了解的人都知道这有多麽伟大的意义。POOMA则是一个专门研究C++并行数学算法的项目，它的前景更加不可限量。译者非常认同B. S的这个观念。--译者）</FONT><BR><BR>有了足够的经验之後，我们就能更好的决定应该对标准做些什麽调整。</P>
<H3><FONT color=#0000ff>18. 显然，这几年世界变了，正在走向一个以Web为中心 分布式计算为主流的时代。那麽您觉得C++还能维持其地位吗？程序员们可不可能把若干种专用语言（比如Perl Javascript）综合运用以彻底取代某一种通用语言？</FONT><FONT color=#ff0000>（C++就是这样的通用语言--译者）</FONT><FONT color=#0000ff>为了配合新的计算模式，C++及其标准库应该做怎样的调整？</FONT></H3>
<P>从来没有哪一种语言能适合所有的工作，我恐怕以後也不会有。实际系统通常是用多种语言和工具构造起来的。C++只是想成为若干语言和工具中的一个，当某些专用语言在其领域里特别突出时，它们可以与C++互为补充。也就是说，我觉得如果大多数现在的专用语言能借助特定领域的C++库共同工作的话，它们会表现得更出色。脚本语言通常导致难以维护的代码，而且也没有给程序的结构 可扩展性和可维护性的优化留下什麽余地。<BR><BR>我不敢肯定未来的代码是否真的会是以Web为中心的。就算是直接处理Web的系统也主要是由处理本地资源，如IP连接之类的程序模块构成的。<BR><BR>地理上的分布性以及服务器软件对於并发机制的高度依赖对於系统的建造者来说的确是个挑战。有些针对上述问题的库已经出现，也许我们将会看到它们最终得以标准化。当然，一些原操作和保证规则应该被加到核心语言中以提供对这些库的更佳支持。<BR><BR>总的来说，对於Web和网络，我们非常需要一个真正的系统/网络级的安全模型。指望JavaScript之类的脚本语言实现这个模型无异于白日做梦。<BR><BR>注意，我也没说C++提供了这个问题的解决方式。C++的重心是高效的访问系统资源，而不是反欺诈。</P>
<H3><FONT color=#0000ff>19. 您看C++未来的走向如何？在接下来的10年里它会衰落吗？或者是基本保持现在的形式？或者发展变化呈不同的形式？</FONT></H3>
<P>C++有 最美好的未来。用它你能写出伟大的代码。除了故意进行恶意欺诈，C++仍将是开发高性能 高复杂度系统的最好语言。据我所知，没有那种语言能在通用性 效率和精致三方面的统一上可与C++相题并论。<BR><BR>我没看到C++有衰落的徵兆。在我能预见的未来里，它的用途还会不断增长。当然，在未来的十年里我们会看到一些变化，但不会像你想得那麽显着。跟每一种语言一样，C++也会发展变化。<FONT color=#8080ff>"语言专家们"要求改进的喧嚣声震耳欲聋，但是系统开发者们的基本请求是保持稳定。</FONT><BR><BR>C++会改进，但是这些改进将主要是为了反映从实践中得来的经验教训，而不会是为了追风尚赶时髦。为了更高效地使用一些新的编程技术，比如通用编程技术，可能会增加一些小的特性。会有大量的库涌现，<FONT color=#ff00ff>我预期会出现一种崭新的 更出色的库支持机制</FONT>。我希望新的扩展主要集中在支持抽象方面的一般特性，而不是为支持某些特殊任务的特定机制。<BR><BR>例如，"属性"这个概念是很有用的，但我不认为在一种通用编程语言中有它的容身之地。用标准C++的一组类可以很容易地支持这一概念。如果我们感觉那族类对於"属性"这一概念的支持不尽如人意，也不会立刻跑去在语言里增加属性机制，而是仔细考虑如何改进类和模板以帮助库设计人员尽可能接近"属性"这个概念。也许通过改进函数对象的机制能够给这个问题一个满意的答复。<BR><BR>为了使C++在接下来的十几年中保持灵活可变，很基本的一点就是不要让标准C++赶什麽学术或者商业的时髦。人们要求增加的特性中很大一部份通过使用现有的标准C++开发新库的方式都可以实现。还有，事实上人们渴望得到的很多特性已经被包括在标准C++中，并且被最新的编译器支持。<BR><BR><FONT color=#8080ff>对许多程序员来说，提高代码质量的最佳途径不是追求什麽语言扩展，而是好好地 慢慢地品味最新的C++技术书籍</FONT><FONT color=#ff0000>（可惜我们到目前为止连这种机会都没有--译者）。</FONT></P>
<H3><FONT color=#0000ff>20. 您怎麽看待脚本语言的兴旺态势？特别是Python，似乎提供了一种学习OO技术的更简单的途径</FONT></H3>
<P>有些语言很不错。比如Python，我很喜欢。但是我认为你从不同的语言中学到的OO技术是不完全相同的。当然，每一个专业的程序员都需要通晓几门语言，并且了解各种语言在编程和设计技术上的不同。<BR><BR>在我看来，用脚本语言建造的系统与用C++那样的通用语言建造的系统大不相同。从两类语言中学到的技术区别明显。在OO技术里也不存在什麽通用部份对於各种系统的高效建造来说都是至关重要的。</P>
<H3><FONT color=#0000ff>21. 有没有计划往标准C++里增加一些新的特性以支持分布式计算？</FONT></H3>
<P>没有，我也不认为有这个必要。用更好的库就差不多能解决问题了。最多，为了支持这类的库，我们可能会增加一些低级的原操作和规则<BR></P>
<H3><FONT color=#0000ff>22. 未来C++有没有可能定一个可移植的二进制接口？</FONT></H3>
<P>如果你说的"可移植"是指跨硬件和块操作系统的可移植，我想回答是不会。我们当然可以设计一个解释器或者虚拟机<FONT color=#ff0000>（如同Java的做法--译者）</FONT>，但这样一来，由於无法以最优的方式访问系统资源，C++的能力就会受到削弱，。我真正希望在不远的将来能够看见的东西是<FONT color=#8080ff>platform ABIs</FONT>（ABI， Application Binary Interface） 。例如，<FONT color=#8080ff>有人正在努力为Intel新的IA64体系定义C++ ABI，我想这些努力会得到用户们的巨大支持。能够把不同编译器产生的代码编译在一起将会是一项十分有意义的事情。</FONT></P>
<H3><FONT color=#0000ff>23. 在不少流行领域，C++正在渐渐失去光芒，因为它要求人们花很大的精力去对付一些很基本的工作，比如管理内存（因为没有垃圾收集机制），管理模块之间的依赖性（因为没有包机制），管理组件的版本。C++缺乏一些现代语言已经视为标准的特性。比如传言中最酷的Java语言就特别重视这些问题。那麽在解决这些问题是否会导致C++的发展背离其根本宗旨呢？C++应该怎样发展以保证我们在这种语言上的投资能有合理的回报，而不是被迫去重新使用另一种语言？</FONT></H3>
<P>我倒还没有注意到C++比以前用的少了。相反，我看到的指标表明C+的使用还在稳定地增长 。<FONT color=#8080ff>只不过这种基数很大的稳定增长以及在标准性 移植性和库方面的不断提高并没有造成什麽具有欺骗性的新闻效应而已</FONT>。我认为你所说的"失去光芒"只不过是市场推销和新闻意义上的现象。<BR><BR>如果你需要垃圾收集机制的话，你可以在C++应用程序中插入一个垃圾收集器。有不少自由的和商业的垃圾收集器已经在重要的实践中被证明是很出色的。<BR><BR><FONT color=#8080ff>如果你不想使用垃圾收集机制，也没关系。你可以使用标准容器类，它们大大减少了对於显式分配和回收内存的需要</FONT>。这样，使用现代的库和现代的编程风格，你能够避免大部份的内存管理问题。<BR><BR>同样的技术还能够用来避免一般资源的管理问题。并不是只有内存才会泄漏，线程句柄 文件 互斥锁 网络连接等都是重要的资源，为了建立可靠的系统，这些资源必须被正确的管理。<FONT color=#8080ff>如果你觉得有了垃圾收集机制就可以解决所有的资源管理问题，那麽你最好赶快从美梦中醒来。</FONT><BR><BR>C++提供了很多机制来管理一般性的资源。关键的手段--"Resource Acquisition is Initialization"<FONT color=#ff0000>(这是著名的RAII惯用法，阅读原文时会经常遇到，其意义是说将所有的资源分配申请放在对象初始化过程中进行，而将资源释放动作放在对象销毁过程中——译者)</FONT>可以使用函数对象来管理生存期问题。语言中关於对象的局部构造和异常机制对这项技术提供了支持。<BR><BR>某些语言的狂热支持者总是用讽刺漫画的笔法描述C++，然而C++实际上要好得多。特别是我觉得很多其他的特性已经泛滥不堪了，在C++中，通常这些特性能够很容易的被模拟出来。相反的，新的语言在推广的过程中总是不断地增加新的特性，<FONT color=#8080ff>这就是为什麽从一种语言诞生到被广泛使用，其体积通常会增加个两三倍。</FONT><BR><BR>目前，最为个人和组织，对於C++的最好投资就是去更好地理解标准C++和现代的C++设计编程技术。大多数人使用C++的方式实际上停留80年代中期的水平，甚至比那更陈旧。<BR><BR>至於模块依赖性问题，我的观点是，在编程语言的工作和系统的工作之间应该有一个明显的界线，依赖关系应该尽可能地与编程语言分开，而由系统来支持。<BR><BR>我不认为组建版本的问题应该由编程语言来解决，这是一个系统范畴里的问题，在语言里应该通过提供相应的库来解决。C++有这样的机制。<BR><BR>解决这样的问题不会使C++偏离轨道。但是给C++增加很多特殊的特性就会使C++偏离轨道，而且在保持可移植性和平台独立性方面也会是一个倒退。<BR></P>
<H3><FONT color=#0000ff>24. 标准C++推出有段时间了，Java也大踏步地往前走而且取得了显着的进步，您现在怎麽比较Java与C++？您觉得Java想要变成像C++一样"好"的语言还需要做些什麽？您举的C++从Java身上学到了什麽经验吗？有没有什麽Java的特性您认为是可以被C++吸纳的？</FONT></H3>
<P>我不比较语言。做好这项工作是十分困难的，而且很少具有专业水准。<BR><BR>我认为C++的进步会是主要以它的用户在使用中遇到的问题以及其自身逻辑为基础。当然，其他语言中的某些思想也会被考虑，但不能被简单的移花接木过来。你必须审视那些机制在技术上和思想上的背景，并且找到在C++中支持这些技术的最佳方案。<BR><BR>有时最好的选择是综合使用几种语言。毕竟没有任何一种语言是放之四海而皆优的。C++现在是，将来也继续会是在广泛应用领域中最好的语言之一。但是，我们不能被拉下水，不能把所有可能的特性都加到C++里面来向大众献媚。我认为Java和C++现在和将来都会是十分不同的语言，语法相似，但背後的对象模型明显不同。<BR><BR><FONT color=#8080ff>对於我来说，一个很重要的区别是C++有一个ISO标准，而Java则是一个专有语言。</FONT></P>
<H3><FONT color=#0000ff>25. 在Java刚刚出现的那几年，有很多欺骗性的言论说它将会是终极语言，会取代C++。您觉得在过去两三年里Java对C++的追随者们有什麽影响？</FONT></H3>
<P>到现在关於Java的不实之辞也还随处可见。暂且不提Java在过去5年间的创纪录的发展，狂热的大众似乎认为Java将最终取代的不仅仅是C++，而且还有所有其他的编程语言。但在另一方面，C++的使用仍在继续增长。我不认为Java对於C++的影响已经使得人们转而把本来打算用来创造更好的C++工具库的资源调过去开发Java工具库。Java对於学习编程的人来说没有太多的新东西，所以对於C++的定义也没什麽影响。在那个领域，Java还得努力追赶。例如，<FONT color=#8080ff>我认为为Sun迟早会往Java里加入类似模板的机制</FONT>。</P>
<P>人们应该认识到C++和Java的目标是何等的不同。 以C++的设计理念来衡量Java，或是以Java的设计理念来衡量C++，得出的结论都不会很好。<BR><BR>在访谈即将结束时，或许我该再次表明态度：C++仍然是我喜爱的语言，在写代码时你会发现没有那种语言能像它那样在如此广泛的应用领域和平台上同时达成如此的高效与精致。<BR></P>]]&gt;<img src ="http://www.blogjava.net/qq13367612/aggbug/16221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-15 12:54 <a href="http://www.blogjava.net/qq13367612/articles/16221.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C#的前途如何</title><link>http://www.blogjava.net/qq13367612/articles/16248.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 15 Sep 2005 04:51:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16248.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16248.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16248.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16248.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16248.html</trackback:ping><description><![CDATA[<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 cellPadding=0 width="100%">
<TBODY>
<TR>
<TD vAlign=top width=32></TD>
<TD style="LEFT: 0px; WIDTH: 100%; WORD-WRAP: break-word; 130: ">
<TABLE style="TABLE-LAYOUT: fixed" height=120 cellSpacing=0 cellPadding=0 width="100%">
<TBODY>
<TR vAlign=top>
<TD style="LEFT: 0px; WIDTH: 100%; WORD-WRAP: break-word; 130: "><FONT color=#555555>如果把C# 和 JAVA 在网络服务领域的争夺比作未来制空权的争夺的话，那么C# 和传统通用快速开发工具——VB、DELPHI等的较量将是地地道道的白刃战。可能最惨的程序员就是VB程序员，在微软，VB就像离任的克林顿，不但失去了所有的光辉，而且乱事缠身。想想吧，VB6写的项目必须用转换工具转换成基于.NET的代码才能在VB7中调入，几乎面目全非。由于VB7遵循为迎合.NET而建立的通用语言规范（CLS），几乎把所有原来只在C++、JAVA等语言中可以运用的特性统统加了进来，只是语法和原来兼容。如果你是第一次在VB7中看到自己的旧VB6项目转换之后的代码，一定要当心你的心脏！所以，努力吧，别告诉我你将就此退休。DELPHI的状况也好不到哪里去，原来的看家本领是做起应用来又快又好，可现在看看最新的VS.NET Beta 1, 你会感到如此熟悉，众多的属性列表、组件……谁让你穷呢，连总设计师都养不住。 
<P>其实在编程语言中真正的霸主多年来一直是C++，所有的操作系统和绝大多数的商品软件都是用C++作为主要开发语言的。JAVA的程序员绝大多数也是C++的爱好者，PHP的成功里面也有类似C++的语法的功劳。在操作系统、设备驱动程序、视频游戏等领域，C++在很长的时间内仍将占据主要地位，而在数量最大的应用软件的开发上，C# 很可能取代C++的位置。首先，C# 和JAVA一样，简直就是照搬了C++的部分语法，因此，对于数量众多的C++程序员学习起来很容易上手，另外，对于新手来说，比C++要简单一些。其次，Windows是目前占垄断地位的平台，而开发Windows应用，当然微软的声音是不能忽略的。最重要的是，相对于C++，用C# 开发应用软件可以大大缩短开发周期，同时可以利用原来除用户界面代码之外的C++代码。 
<P>但是，C# 也有弱点。首先，在大量的现有Windows平台上，C# 的程序还不能运行，因为C# 程序需要 .NET运行库作为基础，而 .NET运行库将作为新一代的Windows（Whistler）的一部分发行， 或以Service Pack的形式提交给Windows Me 和 Windows 2000用户。所以在近期，C# 会主要在服务器上得到应用。其次，C# 能够使用的组件或库还只有 .NET 运行库等很少的选择，没有丰富的第三方软件库可用，这需要有一个过程，同时各软件开发商的支持也很重要。第三，JAVA的成功因素里有一些是反微软阵营的吹捧，虽然“只写一次，到处运行”只是一句口号，但毕竟已经是一种成熟的技术。而C# 的鼓吹者目前只有名声不佳的微软，且只能运行在Windows上。实际上这两种语言都不是不可替代的，理智的说，对软件开发商而言，什么用的最熟什么就是最好的工具。尤其对C++的使用者，C# 没有带来任何新东西，因为.NET运行库在C++中也可以使用，没有要换的绝对的理由。 
<P>综上所述，我个人认为，近几年，C# 将不可避免地崛起，在Windows平台上成为主角，而JAVA将在UNIX、Linux等平台上成为霸主，C++ 将继续在系统软件领域大展拳脚。非常有意思的是，这些语言的语法极其接近，因为JAVA和C# 都是由C++发展而来的。其他的开发工具当然还会在相当长的时间里继续他们的旅程，不过在市场份额上，将不可避免地受到冲击。</P></FONT></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><![CDATA[<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 cellPadding=0 width="100%">
<TBODY>
<TR>
<TD vAlign=top width=32></TD>
<TD style="LEFT: 0px; WIDTH: 100%; WORD-WRAP: break-word; 130: ">
<TABLE style="TABLE-LAYOUT: fixed" height=120 cellSpacing=0 cellPadding=0 width="100%">
<TBODY>
<TR vAlign=top>
<TD style="LEFT: 0px; WIDTH: 100%; WORD-WRAP: break-word; 130: "><FONT color=#555555>如果把C# 和 JAVA 在网络服务领域的争夺比作未来制空权的争夺的话，那么C# 和传统通用快速开发工具——VB、DELPHI等的较量将是地地道道的白刃战。可能最惨的程序员就是VB程序员，在微软，VB就像离任的克林顿，不但失去了所有的光辉，而且乱事缠身。想想吧，VB6写的项目必须用转换工具转换成基于.NET的代码才能在VB7中调入，几乎面目全非。由于VB7遵循为迎合.NET而建立的通用语言规范（CLS），几乎把所有原来只在C++、JAVA等语言中可以运用的特性统统加了进来，只是语法和原来兼容。如果你是第一次在VB7中看到自己的旧VB6项目转换之后的代码，一定要当心你的心脏！所以，努力吧，别告诉我你将就此退休。DELPHI的状况也好不到哪里去，原来的看家本领是做起应用来又快又好，可现在看看最新的VS.NET Beta 1, 你会感到如此熟悉，众多的属性列表、组件……谁让你穷呢，连总设计师都养不住。 
<P>其实在编程语言中真正的霸主多年来一直是C++，所有的操作系统和绝大多数的商品软件都是用C++作为主要开发语言的。JAVA的程序员绝大多数也是C++的爱好者，PHP的成功里面也有类似C++的语法的功劳。在操作系统、设备驱动程序、视频游戏等领域，C++在很长的时间内仍将占据主要地位，而在数量最大的应用软件的开发上，C# 很可能取代C++的位置。首先，C# 和JAVA一样，简直就是照搬了C++的部分语法，因此，对于数量众多的C++程序员学习起来很容易上手，另外，对于新手来说，比C++要简单一些。其次，Windows是目前占垄断地位的平台，而开发Windows应用，当然微软的声音是不能忽略的。最重要的是，相对于C++，用C# 开发应用软件可以大大缩短开发周期，同时可以利用原来除用户界面代码之外的C++代码。 
<P>但是，C# 也有弱点。首先，在大量的现有Windows平台上，C# 的程序还不能运行，因为C# 程序需要 .NET运行库作为基础，而 .NET运行库将作为新一代的Windows（Whistler）的一部分发行， 或以Service Pack的形式提交给Windows Me 和 Windows 2000用户。所以在近期，C# 会主要在服务器上得到应用。其次，C# 能够使用的组件或库还只有 .NET 运行库等很少的选择，没有丰富的第三方软件库可用，这需要有一个过程，同时各软件开发商的支持也很重要。第三，JAVA的成功因素里有一些是反微软阵营的吹捧，虽然“只写一次，到处运行”只是一句口号，但毕竟已经是一种成熟的技术。而C# 的鼓吹者目前只有名声不佳的微软，且只能运行在Windows上。实际上这两种语言都不是不可替代的，理智的说，对软件开发商而言，什么用的最熟什么就是最好的工具。尤其对C++的使用者，C# 没有带来任何新东西，因为.NET运行库在C++中也可以使用，没有要换的绝对的理由。 
<P>综上所述，我个人认为，近几年，C# 将不可避免地崛起，在Windows平台上成为主角，而JAVA将在UNIX、Linux等平台上成为霸主，C++ 将继续在系统软件领域大展拳脚。非常有意思的是，这些语言的语法极其接近，因为JAVA和C# 都是由C++发展而来的。其他的开发工具当然还会在相当长的时间里继续他们的旅程，不过在市场份额上，将不可避免地受到冲击。</P></FONT></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>]]&gt;<img src ="http://www.blogjava.net/qq13367612/aggbug/16248.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-15 12:51 <a href="http://www.blogjava.net/qq13367612/articles/16248.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在较量中携手前行——写给Java 1.5和.NET 2.0</title><link>http://www.blogjava.net/qq13367612/articles/16241.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 15 Sep 2005 02:28:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16241.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16241.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16241.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16241.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16241.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 有些时候等待是一种幸福，就如对于Java 1.5(商业的版本号为5.0)和.NET 2.0(内部版本号为1.2)的守候。对于Java社区，1.5 RC1的等待恍如隔世，终于在所有人快失去耐性的时候给出了这个定心丸；对于.NET社区，则是一种欣喜，反过来也是一种担忧，毕竟相对稳定的.NET 1.1版本还没有Java社区中那样成熟的姿态，却要考虑是否迈进2.0的新世界。         不知道...&nbsp;&nbsp;<a href='http://www.blogjava.net/qq13367612/articles/16241.html'>阅读全文</a><img src ="http://www.blogjava.net/qq13367612/aggbug/16241.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-15 10:28 <a href="http://www.blogjava.net/qq13367612/articles/16241.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C# 与 C 、 C++ 、 D 、 Java 的性能比较 [第二部分]</title><link>http://www.blogjava.net/qq13367612/articles/16242.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 15 Sep 2005 02:24:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16242.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16242.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16242.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16242.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16242.html</trackback:ping><description><![CDATA[<P>在本书的第一部分，我讲述了C#语言的许多基本方面——包括整型、字符型和浮点型之间的转换，循环、迭代算法，以及字符串比较、拼接和 特定符号统计 。正如我所说，对一种新的语言与它的类似语言进行全面、公平的性能比较，范围将会很庞大，所以本书沿用第一个例子并只对有限的重要的部分特性做测试。这种比较，以先前的 C#及它的库与C、C++、D和Java的特性对比为基础，主要在以下几个领域： </P>
<P>·类机制（封装与模板） </P>
<P>·异常 </P>
<P>·内存— 重点 </P>
<P>·大型文件的随机访问 </P>
<P>·资源获取初始化 </P>
<P>正如我在第一部分提到的，这里所做的测试只是针对程序性能的一小部分，硬件及操作系统的配置是测试语言 /编译器/库函数的机器的典型配置：2-GHZ，512-MB，Pentium IV，Windows XP Professional。 这里我们看到的任何结果揭示了在其它环境下可能的性能，这不说明可以假设这些结果就是正确的典型例子。 最后要说明的是，当我提到“C#比Java好”等结论的时候，一定是指“在特定的测试环境的条件下”，这一点请你铭记在心。 </P>
<P><STRONG>性能评价 </STRONG></P>
<P>所有的代码还是用和第一部分一样的编译器编译。 C#代码在.NET框架 1.0.3705（可从http://microsoft.com/netframework/免费下载）下，由Visual C#.NET 编译器v7.00.9466编译。D代码由Digital Mars D 编译器 Alpha v0.62 (可从http://www.digitalmars.com/免费下载) 编译。Java代码由J2DKSE1.4.1-02 ( 可免费下载 http://java.sun.com/ ) 编译。 C和C++代码 ( 单线程 , 包括 <STRONG>raii </STRONG>, 和静态链接库 ) 由使用 STLPort v4.5的Digital Mars C/C++ v8.33 ( 可从 http://www.digitalmars.com/免费下载 ) 和使用 Visual C++ 6.0的头文件和库文件的Intel C/C++ v7.0来编译。所有这些编译器均使用最优化处理 ( set for speed ) ; Intel 用-QaxW flag [2]提供两套编码路径：一个是针对有SSE2的Pentium IV处理器，另一个是针对其他所有的处理器——在运行时选择合适的一个。（因为Intel编译器只适用于Intel处理器构造，特别是在这个测试中的Pentium IV，这编译器的编译结果必须在此基础上评价，不能在其他的处理器上进行同样的过程，比如Athlon，甚至是旧版本的Intel处理器也不行。） </P>
<P>后面将要描述我们测试的假想的五种例子中的性能表现。他们都用2-GHZ，512-MB，Pentium IV，Windows XP Professional的配置，并沿用在第一部分用的性能分析系统。每一种语言所编程序的调试都执行7次，同时没有其他占线过程，去掉最高速的和最低速的，纪录平均值。所有的时间信息，通过仅区分相应时间区间的高性能计时器保存下来。（C用户调用Win32 API <STRONG>QueryPerformanceCounter() </STRONG>；C++ 用 Win-STL ' s <STRONG>performance_counter </STRONG>[4], 可在http://winstl.org/找到 ； C# 用 <STRONG>SynSoft.Performance.PerformanceCounter </STRONG>，可在 http://synsoft.org/dotnet.html找到； D 用 <STRONG>synsoft.win32.perf.PerformanceCounter </STRONG>, 可在http://synsoft.org/d.html找到；Java 用 <STRONG>System.currentTimeMillis() </STRONG>.）每一个测试程序都包含一个启动迭代的程序，这样可以将启动或缓冲时的影响最小化。 </P>
<P>在每一个例子中的C#，C++和D的特征: C在所有 保存 封装和 <STRONG>raii( </STRONG>类)的地方出现，因为它没有这两种例子必需的语言特征。 <STRONG>raii </STRONG>没有出现Java，因为Java甚至连 Resource AcquisitionIs Initialization (RAII [1])的机制也没有，所以它仅仅作为在GC-kicking的一个练习。简单的说，我不会展示所有的语言的每一个例子的所有特性，但是所有这些都包含于在线记录中（连同产生他们的Digital Mars make-compatible make-file）。 </P>
<P>下面就对测试性能的例子进行评价 : </P>
<P><IMG height=306 src="http://mag.vchelp.net/200312/images/part2/image001.jpg" width=553><BR><STRONG>封装 </STRONG>。这个例子（ Listing1）的目的是比较不同语言（除了C）在类机制上的代价，这些语言并不完全支持相对于它们所做的所有类型的一般处理。尤其是C#和Java一般不能存储和使用它们的内置类型实例，比如int，long，bool等等，同样，对于从这些实例派生的类型也不行。相反C++和D都支持模板，它可以给任何类型的一般性操作带来方便。在以后的文章里，我将详细地讲解容器。但在这个章节将不会用到它，如果提及它反而会分散对于封装意义的理解。C#和Java中未封装的变量传递ints给异或函数，封装变量传递整形值在Int32（C#）和Interger（Java）的实例中。注意Java不支持模糊的封装（这被证明是好的因为编译器将对于这种封装带来的低效给予警告），所以实例必须明确地被创建（在列表1中标记“Java ” 处可以看到）。 C++和D的运行是简单的使用模板，这就是它们的泛型机制。（在这里我不对“一切都是对象”的模板的好处作深入探讨，但是我将在这期的姊妹篇Stepanov camp中对它做详细讲解）。 </P>
<P><STRONG>异常处理 </STRONG>。异常的语义在各种语言之间有很大的区别。 在 C++，异常处理和调用析构函数一起沿着堆栈操作下来，然而在有垃圾回收的语言中，这些对象被丢弃，直到将来的某一时刻被收集。 这样，就无法对各种语言进行有意义的比较。所以在这个例子中，只能通过将一段 using/aware代码与另一段没有异常处理支持的相同代码进行比较。这个例子有三种测试，它们的目的就是去衡量使用和抛出异常处理的代价。第一（Listing 2）是比较使用异常处理和通过函数返回值来传递信息给调用者这两者的代价。第二（Listing 3）是评估在try-catch模块中执行的代价（尽管没有异常处理被抛出）。最后（Listing 4）就是评估遍历执行try-catch模块的代价（同样，这没有异常处理被抛出）。 </P>
<P><IMG height=362 src="http://mag.vchelp.net/200312/images/part2/image002.jpg" width=554><BR></P>
<P><IMG height=549 src="http://mag.vchelp.net/200312/images/part2/image003.jpg" width=381><BR></P>
<P><IMG height=270 src="http://mag.vchelp.net/200312/images/part2/image004.jpg" width=388><BR></P>
<P><STRONG>Mstress。 </STRONG>这一个例子的目的是，在假设分配比回收优越的基础上，看看垃圾回收语言是否在高的内存利用率上有重要的非线形关系。这里存在四种测试，每一个都牵涉到分配与释放（ C 和 C++）或者丢弃（C#, D, 和 Java）在一个时间段内的内存块。这些块大小是通过相同的伪随机数算法生成的（详见文章的第一部分），这能使块的大小处于1至1000之间，从而在确保每种语言在相同的环境下测试时仿效现实世界中的分配。第一种测试（Listing 5）通过一个不断循环的变量分配了1000个内存块，目的是看看频繁地分配会不会“留下”更多的碎片，这些碎片会阻碍分配的进行直到突然产生明显的GC收集。第二种测试（Listing 5）增加了1000次循环中分配的次数，以测试由不断增加的内存负担引起的性能的 损失 。 第三和第四种测试（没列出来）跟前两个相似，它们增加了另外的内存块和分配/回收的交叉活动，目的是为了模拟不断增加的内存碎片。 C使用malloc()/free()，而C++，C#，D和JAVA用new[]。 </P>
<P><STRONG></STRONG></P>
<P><IMG height=268 src="http://mag.vchelp.net/200312/images/part2/image005.jpg" width=554><BR><STRONG>rafile </STRONG>。这一个例子（ Listing 6）评估各种语言从大文件中随机读取的能力。它利用某种伪随机算法（详见文章第一部分）来模拟在大文件中的查找/读取。这里用到了两个文件，一个4.2MB，一个21.1MB。在每一个查找点，四个字节被读取，并且和一个运行值进行异或运算（这样有利于证明所有的语言以相同的方式查找和读取）。有意思的是，原始的运行程序读了32位的整数，但不幸的是，JAVA用了network byte-order而不是host byte-order，这与C, C++, 和D的machine-order reading是不相容的。 </P>
<P><IMG height=240 src="http://mag.vchelp.net/200312/images/part2/image006.jpg" width=554><BR></P>
<P><STRONG>Raii（类） </STRONG>： 这最后的例子目的是为了评估没有内建支持这个非常重要的语言特征（很不幸 C#和Java都没有）而付出的代价。此例子表达了通过资源句柄XUser 类型从XFactory资源管理器分配X资源到客户端代码(见代码段7)。在C++和D语言中，XUser 提供一个析构函数，在析构函数里分配的资源自动地回收到资源库，资源在这种情况下是个信号量，.NET和D的执行代码可从 <A href="http://synsoft.org/dotnet.html">http://synsoft.org/dotnet.html </A>和 <A href="http://synsoft.org/d.html">http://synsoft.org/d.html </A>下载。C#、D和Java 提供它们自己抽象的同步机制，但我需要一个信号量去引起交叉线程的竞争，去保证同样的同步机制以便所有语言的进行生动的比较。此信号量在每种语言执行代码中都有最初可获得的五个键值。 </P>
<P><IMG height=716 src="http://mag.vchelp.net/200312/images/part2/image007.jpg" width=553><BR></P>
<P>尽管 C#的XUser 类有一个析构函数，但此析构函数仅在实例进行垃圾碎片收集时被调用。（C#使用析构函数的语法，但编译器把它翻译成 <STRONG>CLR </STRONG>（公共语言运行时刻）的 <STRONG>Finalize() </STRONG>方法。）这种情况跟 Java一样。C# 还尽量更多地支持习惯用语（虽然它支持的还不够），这是通过使用引用声明来实现的，正如表7所示。（为了方便，“using-ed”类型必须实现 <STRONG>IDisposable </STRONG>接口，否则声明无效），一个明显的问题是类的使用者必须承担确保语义的正确的负担，而不是类的开发者。这是奇怪的二分法：C#是面向对象的，但它依靠类的使用者对类的内部执行代码的足够理解以决定是否需要此类的直接析构。C++和D语言过程明确，由类自己决定：C++通过析构函数的执行，D提供析构或终止函数，类声明验证关键字 <STRONG>AUTO </STRONG>（没有这些，它按C#和Java的方式运行正常的垃圾碎片收集程序）。 </P>
<P>C#语言使用 <STRONG>USING </STRONG>声明（和 <STRONG>IDisposable </STRONG>接口）的一个可替代方法是依靠垃圾回收。在形成这个情况之前，我假设当内存使用率达到触发阀值或者主线程空闲/等待的时候启动垃圾碎片回收。（我不知道这种误解是怎么形成的，或许只是异想天开）。当我在执行 <STRONG>Raii2 </STRONG>例子的最初版本的时候（变量没有使用 <STRONG>USING </STRONG>声明），处理进程马上挂起。很明显，在其他线程堵塞时垃圾碎片收集没有触动，只有在内存消耗到一定程度才触动。因此，为了程序执行，我们被迫在另一个工作线程中自己剔除垃圾碎片收集。当然，这在实际编程中是非常可笑的执行策略，执行结果证明了这一点。 <BR><BR></P>
<P><STRONG>结论 </STRONG></P>
<P>大部分的结果是以绝对运行时间数（全部循环或每循环）或者 C#的执行时间和C，C++，D，Java分别的百分比来表达的。在后一种方式，围绕着百分刻度的结果表示C#的性能比率，高的值表示C#的性能优越，低的值表示相对低下。 <STRONG>Raii </STRONG>的情况是个例外，在这里结果是以C++（Digital Mars）时间的百分比表示的，由于垃圾碎片收集代码的消耗，调用时间值是以对数刻度表示的。这说明在对数比例上归根于非常高的垃圾处理花费. </P>
<P><IMG height=332 src="http://mag.vchelp.net/200312/images/part2/image008.jpg" width=502></P>
<P>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD width=41></TD></TR>
<TR>
<TD></TD>
<TD><IMG height=288 src="http://mag.vchelp.net/200312/images/part2/image009.jpg" width=422></TD></TR></TBODY></TABLE><BR><STRONG>BOX </STRONG>. 从图 1中我们能看出,在合理范围里(C#语言的97%-271%)对于所有编译语言解除封装操作(就简单整形数据)成本的差别：最差的是C++(Digital Mars)，成本是C#的271%,最好的是C++(Intel) 成本是C#的97%.假设执行相对比较简单的 <STRONG>异或 </STRONG>运算, C#几乎可以像 Intel那样出色，你对这个并不会感到很惊奇，但你相信C＃要比C++(Digital Mars)和D模板实例快两倍.我有些惊讶C++(Digital Mars)这个相对弱的性能,特别是和D比起来它要慢得多，然而他们出自同一开发商。 
<P></P>
<P>显然这个封装花费是有意义的 ,相当于C++模板花费4-10倍.这个测试仅仅包括整数,相对来说可能不能完全反映特殊类型.虽然如此,我们能有把握断言这个模板轻易赢得了性能战.有趣的是就他们各自的成熟度而言，C#要比java稍好, </P>
<P>差距并不大 (小于4%).(我想可能由于.NET处理目标JIT3。【3】)。更有趣的是,事实上在未封装的形态下,c#明显比java快. </P>
<P><IMG height=306 src="http://mag.vchelp.net/200312/images/part2/image010.jpg" width=424><BR></P>
<P>除了图 1以外,有人认为,不抛出的异常variants都是是比抛出的异常副本要快的(如图2). 用 c和c++,Intel完成任务要比所有其它不抛出的异常variants快.c#紧随其后。似乎c,c++,D(不考虑编译器)在异常处理上花费大致相等,符合win32异常处理机制的预期限制因素.我们能看到三种语言在抛出异常variants上的差别是比较小的,很可能的原因是由于有相关的额外的管理消耗关系 (c++规定执行破坏功能的堆栈是从捕获点到抛出点的来回). </P>
<P>我发现有趣的是在抛出异常variants方面c#和java的执行是有关系的. </P>
<P>因为这两种语言语义在异常抛出处理要胜于返回当前错误值 ,也胜于其它语言, </P>
<P>尤其 c语言所呈现的关系执行调用.我非常赞同Brian Kernighan和Rob Pike </P>
<P>(<>,Addison Wesley,1999)所说的例外仅仅在例外条件的使用,并不是必然会发生的.然而人们能容易看到为何服务器在处理巨大数量的数据的时候可能出现的例外,而经常担心性能的问题.正是如此,java以小于其它语言25%的消耗给人深刻的印象.由此我们限定win32,采用两个有代表性的差不多的c编译器来构造例外处理,虽然没有定论,但是我认为java使用了不同的机制. </P>
<P>那么c#的异常抛出性能如何呢?在相关条件下,相对其他语言而言，使用异常处理语句是不使用异常处理语句的21-77倍，而c#则是191倍;鉴于Java的出色表现，.net工具应该引起注意了。 </P>
<P><STRONG>Except-2. </STRONG>这个图表是为了弄清楚在一个 try-catch范围内异常处理语句的执行是否耗时,如图3,大多数语言的表现同预期的一样:c、c++和D对于Digital Mars是类似的;Intel c 和c++比其它都好.c#要比java快.相比较就性能而言, </P>
<P>c#是很优势的,令人感兴趣的是，c#在进行异常处理是耗时比不进行时还要少.产生这种结果肯定是有原因的(这个结果是经过了多次验证的), 但由于对于c#异常处理机制我没有很深的认识，我不能做出合理的解释.同样,虽然并没有定论,对于D来说，是否进行异常处理对结果似乎并无影响！. <IMG height=375 src="http://mag.vchelp.net/200312/images/part2/image011.jpg" width=508><BR>我认为这是因为它不用清除栈内数据， java也有大致相同的自由度，并且表现出的差异也并不大。 </P>
<P><IMG height=375 src="http://mag.vchelp.net/200312/images/part2/image012.jpg" width=511><BR></P>
<P><STRONG>except-3 </STRONG>如图表 4所示,测试结果与预期大体相符。除了D编译器的结果中微不足道的差异，所有的语言在遍历异常处理语句的时候都比不进行是耗时要长，尽管如此，我仍然对在这两种情况下差异如此之小感到吃惊。我们通常认为使用异常处理机制会耗费时间，并且我认为遍历异常处理语句会在启动和完成上耗费大量时间；事实上，我一开始就预期两者的差异将会非常地显著。然而，在这个测试当中，两者的差异相当不明显。 </P>
<P>但是这个结果并不难理解，导致差异如此小的原因首先在于：这次测试太过于纯粹。这次测试的所有异常处理（类型）都是 int型的。尽管并没有异常被抛出，这样做的目的在于避免在异常处理体的构造（和析构）上耗费时间和消除各编译器处理机制不同所造成的影响。其次，本次测试中没有基于框架的结构体，因为在C++中，尽管析构函数并不会真正被调用，析构函数调用方面的准备也是必须进行的，这样也会造成时间的耗费。（注意析构函数无论异常是否被抛出都会被调用。）这些耗费都会造成测试的不公平，所以我们选择了int型。注意到程序不论是因为异常抛出而中止还是正常退出，析构函数都是要被调用的。所以它仅仅做为勾子而被添加进这些额外的函数调用中。然而，我认为所有的这些因素并不是很充足，它们仅仅使我们可以从那张表里知道当不使用的时候异常的开销是非常小的。很自然，这也正确的表明了一个人对所有语言性能(perform)的期望，当然我们要给它足够的信任. </P>
<P><STRONG>metress-1 </STRONG>这个测试很清楚的表明了在不使用内存分配机制的展示 (exhibit)中不断增长的对固定数量的内存快（随机大小）分配/释放的循环的反应是非线性的。事实上，在很大程度上它们从没有线性(增长)的趋势:从性能上来说并没有什么变化(见图5),除了在一些低循环(low interations)上有所不同之外.(有趣的是，那些低循环的非线性是有意义的--占到了全部命令的50%还多--当然这仅仅是对c#和java而言).不管内存分配机制是否在每次特循环结束后都立即恢复对1000个内存块的释放,或者仅是简单的把它们交给GC在以后的某个时间处理,语言/运行库总是看上去几乎完全不受这些循环执行的影响. </P>
<P>在这组性能相对的测试中，我们可以清楚的看到一些它们之间的不同。在使用 Digital Mars 分配方式的语言中，C和C++的表现是最好的。Visual C++的运行库比使用Digital Mars的语言低大概2.5-3倍，对于使用Digital Mars 的语言和Visaul C++运行库来说，C和C++基本上是相同的。最后,很明显，Java 比C#慢了3-4倍，而比C慢了差不多7倍. </P>
<P><IMG height=393 src="http://mag.vchelp.net/200312/images/part2/image013.jpg" width=514><BR></P>
<P><STRONG>mestress -2 </STRONG></P>
<P>就像我们从图表中看到的，在一定数目的分配释放内存的循环中内存块（随机大小）的不断增长的反应是变量的引用是非线性表现的。这正是我们所希望的，因为在每次循环中分配的内存总量是按指数方式增长.每次循环所分配的内存的大小都低于10，000块.使用Digital Mars分配方式的C 和C++的效率依然是极为优秀的。只是效率表现上低于平均值，而这里也不得不提到java，它的表现同样不好.Visual C++的运行库（C和C++都适用）相对于C#，D 和Java来说，有一个不错的开始，但它很快就降到了一个很低的水平上。 </P>
<P>Java在每次循环是使用10，000内存块之前的表现非常有竞争力，并在这一点急剧上升。D在整个测试中都几乎有着一致表现.几乎一直都是非线性的，这与C#很接近。 </P>
<P>如果可能的话，我都希望看到一条总够长的X轴，C#在内存溢出的情况下仍然保持每次循环不超出10，000内存块的水平并预防这种情况的出现。（D和Java似乎也做的到，但那也只是类似C#的行为一旦被发现就中止测试。) </P>
<P><STRONG>mstress-3 </STRONG>和 4 从前两个测试(variants)看,除了时间的的增长，反复的交叉存取和块变量在执行上的表现并没有什么不同。曲线的走势或者说不同的语言/运行库的相对表现在这一点上没有什么明显的改变。 </P>
<P>我认为C和C++，在使用外部free/delete的条件下，重新使用的新近释放的碎片。相反的，我很难想像出C#,D和Java 是如何使用垃圾收集机制周期性寻找每次循环所分配的内存从而尽可能的减少由内存碎片所引起的负面效应的,或者在这个跟本就没有产生碎片.除去这些不同，这两种方式的表现还是很相似的。 </P>
<P>这只是一种理想的我们所希望的分配机制的表现 ---毕竟，那是一个很极端情况，所有内存分配都可以在给定的周期内完全返回---虽然程序的执行都达到的目的。 </P>
<P><STRONG>rafile. </STRONG>这次测试中我所希望是那些语言实际 (效率)上并没有什么不同.除了C++的执行也许比其它的低上几个百分点. </P>
<P><IMG height=376 src="http://mag.vchelp.net/200312/images/part2/image014.jpg" width=515><BR>图表 7中可以看出，C#的在文件的随机存取上比C(使用Digital Mars)要好,但低于C++(使用Intel和VC6的连接库),和D与Java现表现基本持平。但是C++运行库表现出令印象深刻的性能。 </P>
<P>从所有这一系列测试来看，Intel 似乎已经能够产生性能不错的代码了。但是它的连接的运行库头文件却是Visual C++ 6.0的，这个（大概）不是由Intel编译器产生的。因比它几乎是以压倒性的性能优势超过了DMC，这主要是由于各自的Digital Mars 和微软Visual C++运行库的性能。(我承认有点吃惊，对于些测试结果正好可以反对两个卖家的名声--应该或是不应该得到的)。这也表明一个人的偏见是非常没有理由的。 </P>
<P>另一件值的注意的事就是对不同大小文件访问的开销都非常的小。这也表明所有的语言都利用操作系统的优势很轻易的达到了相同的程度。 </P>
<P><IMG height=400 src="http://mag.vchelp.net/200312/images/part2/image015.jpg" width=529><BR></P>
<P><STRONG>raII </STRONG>. 从图 8中我们能看出使用statement的C#的表现只有C++析构函数效果的一半。D的性能与之相当，虽然产生的代码中有错误（或者是D的Phobos运行库),当进程创建的对像超过32，000左右的时候会使该进程挂起，从而防止过多（一个以上的）通讯中的数据点被确定的使用.在这组单独的测试中我们可以看到RAII对C#和D的支持是很完善的，但并不如想象中那样优秀。如有你要做很多scoping--并且你想，也许要足够的robustaness的帮助---你最好还是选择（stay with）C++。当依赖于.NET的垃圾回收机制时，因为处理过程只是简单地被挂起，所以C＃的性能比较难以测算。可是我们不希望.NET经常调用它的垃圾回收线程，但我找不到理由解释为什么回收线程不以较低优先级在内核中等待，当有垃圾对象需要被回收和主线程空闲时再进行有效的回收。当然，我在垃圾回收方面不是专家，并且可能有一种合理的理论原因说明这为什么不是一个好的处理方法。 </P>
<P>GC（垃圾回收）即使响应的更灵敏，我们可以在它的性能结果上看出效果并不与之匹配。对应第二组数据指针的方法表明了GC每隔1ms被触发一次。 </P>
<P>GC（垃圾回收）的方法比使用使用声明慢了近千倍，所以这不仅仅是一个技术问题的范围。自然，这跟关于这个例子的假设有关，同时跟几乎每个人都用垃圾回收机制去管理频繁争用的资源的释放这个不辩的事实有关，但我没预计到这个变量的性能是如此之差。同时这也给了读过Java/.NET书籍的C++程序员对于书籍建议（或许说依靠）使用终止函数来清除资源而感觉疑惑的一个解释。 </P>
<P><STRONG>结论 </STRONG></P>
<P>在第一篇文章，我对于不同的环节得出不同的结论，这些环节或是语言本身造成的，或是库或者二者造成的，在这里我也这么区分，因为语言特征而产生的影响的部分通常涉及：异常，封装 /模板和RAII的实现。文件存取部分可以看作直接对库函数的操作。（没有任何一种语言阻止写替换操作，虽然这样做并不是很容易）。内存管理部分受语言和内存的影响――虽然我不清楚可以用哪一种管理机制去替代C#和JAVA的缺省的内存管理机制，但是对于其他语言这是很简单的。 </P>
<P>封装。当我们比较封装和模板时，我们可以看出模板明显比封装出色很多。我相信这不会让大家感到很惊奇，但是事实上封装消耗的系统资源是模板的十倍。自然，作为模板类库的作者（ <A href="http://stlsoft.org/">http://stlsoft.org/ </A>），我得出这个结论也许带有偏见，但是这些数据在表示更广泛的情况时，他们自身就说明这一点。在后面，我将说到容器和算法，我们将看到比预计更多的这样的结果。 </P>
<P><STRONG>至于有关异常情况，我想讲四点： </STRONG></P>
<P>1.在所有的程序语言中，使用异常处理从调用的函数过程中返回，而不是保留他们作为异常事件的指示，将导致执行花费巨大的成本。 </P>
<P>2.C#的异常处理机制相对于其他被测试的语言效率是极低的. </P>
<P>3.在有异常处理的上下文环境下运行（比如用try-catch的情况）对于性能没有多大影响，除了C#，它为了提高性能（实际上，我对于这种结果很不理解，并且怀疑这是.NET运行期的一个人为结果而不是C＃/.NET的一般特征。） </P>
<P>4.交叉于异常上下文的执行（比如说进入try-catch和/或 离开try-catch）对于系统性能的影响基本上是非常小的。 </P>
<P><STRONG>Raii. </STRONG>C#支持的RAII例子比C++在性能方面差，虽然不是很多，但与D相比差别无几，基本一致。（这种一致指出了在处理堆分配对象的确定性析构时的基本限制）然而，从理论观点，或从易用性和/或稳健性的实践观点来看，这里还是有很大差距的。C语言缺乏机制可以解释为年代已久，并且它是一种程序语言。很遗憾，java缺乏这种机制，但是它只是可以解释为忽视了。（至今为止我们已经用java8年左右了，所以“忽视”可能也有些牵强。）C#在这方面的努力还不成熟（因为它更多地依赖类的用户而不是类的作者），很奇怪的是C#/.NET很多优点都是在Java中被看作瑕疵/遗漏的地方，比如属性，输出参数，和无符型。 </P>
<P><STRONG>Mstress. </STRONG>这个内存测试的目的是证明如果频繁的内存分配加上垃圾回收机制是否会导致—— C#, D, 和Java这些包含垃圾回收的语言严重的非线形，这明显没有。这个结果可以看出所有的语言/库表现的非常合理的。这里我们可得出几个有趣的结论： </P>
<P>1.C语言和C++语言，提供正确的库支持，他们内存分配空间最快。毫无疑问，部分原因由于它们没有初始化字符数组的内容。根据结果，我重新对C语言进行测试，用calloc()替代malloc()，测试结果很接近C#，虽然仍然会高出5个百分点。 </P>
<P>2.内存碎片（只要轮流存取第三和第四个变量）不会在很大程度上影响内存分配的性能，不会增加总体负担。 </P>
<P>3.如果垃圾回收器安排在没有使用的内存区之后不执行（我假设这是可以的），这将不会对性能有太大的影响。我假设，这说明了C#是第一个会内存枯竭的语言，所以我们可以假定它们通过使用大量的内存空间在内存分配性能方便取得平衡，并且相信在现实的环境中有这种机会运行垃圾回收。 </P>
<P>4.通常更希望体面地降低性能―就象C（Digital Mars）的方式――而不是在各个方面都有很强大的性能，然后在某些未知的阀值化为乌有：在服务器环境下，某一时刻提供一个慢点的服务比导致崩溃可能要好。由于对Digital Mars和Visual C＋＋，C和C＋＋的运行实际上是相同的，我们可以假定因为与通过new操作符的调用的交流而增加的成本可以忽略，并且在C和C++之间没有本质的区别。 </P>
<P>5.C#内存分配时间会比Java快上3～4倍。 </P>
<P>总的来说，这个结果并不象我想象的那样：我期望C和C++在中小应用比 C#，D，和Java稍微落后，但在大型应用中远远优于。真是学无止境。 </P>
<P><STRONG></STRONG></P>
<P><STRONG>Rafile </STRONG>. 对于文件的随机存储结果可以看出一些重点。我认为最重要的一点是仔细的选择库。之所以C++的运行效果看上去要比C和其他的语言好很多，就是因为库的原因。当把C＋＋的性能（Intel 和VC6库）和其他语言/编译器进行比较时，用其他任何一种都很给人留下深刻的印象。自然，这边的例子是故意的，但这么大的性能差别的事实——比C#和 Java快23倍，我们可以期待在现实情况中性能的有意义的差别。（这里，再次证明了偏见是错误的：我很厌恶所有的io流，从很多方面很容易能看出它的效率很低，当然也不能说全部这样。我对于这样的性能印象很深刻）。 </P>
<P>详细的总结了C#的性能以后，我想就我们在其他语言方面的研究发现做一个简短的介绍。 </P>
<P>1.C处理异常事件是不错的，特别是在存储和文件（提供正确的库连接）有非常好的表现；它能提供所有我们能预期的效果，但是与后来的高级语言相比，吸引不了更多的学习者。 </P>
<P>2.C++处理异常事件也不错，特别是在存储和文件（提供正确的库连接）有非常好的表现，而且包含模块的概念和有效的RAII；它始终得到我的钟爱，我认为它在未来的很长一段时间内都是编程的最佳选择。 </P>
<P>3.D在异常事件处理上不是很太好，在文件处理上处于一般水平，在存储方面有比较合理的相关性能和非常不错的线性度，有模块和RAII（虽然语言本身有很多的bug产生，使得很多的处理过程被挂起）；当这语言经过它的初级阶段，期望会得到不错的效果。 </P>
<P>4.Java在封装和内存管理的性能上表现最差，不过它有好的异常事件处理，但没有一点模块和RAII的概念；它不指望会达到真正的美好，我认为C#/.NET可以衡量出它的高低，至少运行在Windows平台上 。 </P>
<P><STRONG></STRONG></P>
<P><STRONG>摘要 </STRONG></P>
<P>从第一部分开始，就给出全文的延伸的意思，从性能方面，如何能更好的用C#/.NET写出很好的软件。在第一部分里，一些结果虽然不是很值得注意，但是有很多的地方还是令人叫奇的（至少我是这样的！）。 </P>
<P>这些研究结果展示了C#可以提供非常好的性能效果——至少在我们测试的范围内是这样——同时不能把它如同象过去把Visual Basic和一些扩展版本的Java当作一种性能差的语言来对待。对我而言，我比较注意语言运行的性能，当然，他们的表现也会超出我的预期效果。我所提出的严肃评论是从语言特征的观点和对多个编程范例的支持方面得出的。 </P>
<P>作为一个最初使用C++的程序员，我从特定的观点得出这些比较结果。这可以从RAII和封装测试以及它们的语言解释上看的出来。在某种意义上来说，这就好比比较苹果和橙子，对于不同背景的人们可能会对我为什么如此强调模板和确定性析构感到疑惑，没有这些他们也进行的很好。 </P>
<P>毫无疑问，模板给C++带来了非凡的革命，它能支持群组合作，或者独立运行，范例是无可比拟的。Java由于缺少模板和强制要求任何东西都是对象，而被批评了很长时间。.NET框架在这方面做法一样也很让人失望；可能由于Java缺少模板而他们可以在.NET环境下得到（他们确实达到）让更广的开发团体感到信赖并接受了它。 </P>
<P>缺少对RAII的支持对GUIs（通用用户接口）是非常好的，这是在严格的框架内操作的软件——即使象J2EE这样精密复杂和高吞吐量的语言，和非关键的程序。但复杂的软件不必要因为它而复杂。在最好的情况下，到处都是最终模块，只要在Finalize()里函数里添加Close()方法就可以了。在最坏的情况下，滞缓或者随机的资源泄漏都会导致系统的崩溃。更甚于以上的，如果OO（面向对象）——就象C#和Java所有都是对象——是你的目的那更让我不安，第一个要失去的就是对象自己清除自己的责任，我没办法理解这个。（我知道一个非常出名的C++程序员——当然我不能告诉是谁，他告诉我当他在课程中突出RAII和模板的重点时，那些非使用C++的人们露出的滑稽表情，让他感觉好象他丢失了什么东西） </P>
<P>D是使用垃圾回收的语言，默认是非确定性析构,有很多地方与C#和Java相似，但是尽管如此，它还是兼容支持RAII的习惯同时具有模板。并且它是一个人写的！我不明白为什么我们对Java或者C#（.NET其它语言也是一样）印象如此深刻，即使有它们支持RAII和模板的缺点，而我们又能比较这些。有可能C#/.NET成功的原因和Java一样，有大量的，有用的库文件（C和C++应该从中学习，D应该去发展），和有一个强有力的后台。 </P>
<P>最后，我想说对于所有的性能结果的比较分析，你必须明智地使用这些结果。我努力使自己公平，选择我相信是公平和有意义的测试。但你一定要判断的出这些例子仅代表了广大可能性的的一小部分（不是无限意义上的），它们不是实际的程序，仅仅是测试而已并且把它简化了，其中不可避免的带有我个人的某种偏见在里面。通过逐步的方法，我希望降低这些因素。但你在阅读和使用这些结果的时候要保持留意。 </P>
<P><STRONG>感谢 </STRONG></P>
<P>我要感谢 Walter Bright提供给我最新的Dv0.62版本，能够完全的测试异常事件。感谢Intel的David Hackson给我提供了C/C++编译器。还要感谢Scott Patterson帮我选择了切合实际的测试方法（他总是不断的在我烦躁、偏题、愚蠢的时候提醒我）。还要感谢Eugene Gershnik 对于我的一些断言给了我严厉的反驳，帮助我适当地注意一些防止误解的说明。 </P>
<H1>Notes and References </H1>
<P>[1] The C++ Programming Language, Bjarne Stroustrup, Addison Wesley, 1997 </P>
<P>[2] The Software Optimization Cookbook, Richard Gerber, Intel Press, 2002 </P>
<P>[3] Applied Microsoft .NET Framework Programming 1 st Edition, Jeffrey Richter, Microsoft Press, 2002 </P>
<P>[4] Described in “ Win32 Performance Measurement Options, ” Matthew Wilson, Windows Developer Network, Volume 2 Number 5, May 2003. </P><![CDATA[<P>在本书的第一部分，我讲述了C#语言的许多基本方面——包括整型、字符型和浮点型之间的转换，循环、迭代算法，以及字符串比较、拼接和 特定符号统计 。正如我所说，对一种新的语言与它的类似语言进行全面、公平的性能比较，范围将会很庞大，所以本书沿用第一个例子并只对有限的重要的部分特性做测试。这种比较，以先前的 C#及它的库与C、C++、D和Java的特性对比为基础，主要在以下几个领域： </P>
<P>·类机制（封装与模板） </P>
<P>·异常 </P>
<P>·内存— 重点 </P>
<P>·大型文件的随机访问 </P>
<P>·资源获取初始化 </P>
<P>正如我在第一部分提到的，这里所做的测试只是针对程序性能的一小部分，硬件及操作系统的配置是测试语言 /编译器/库函数的机器的典型配置：2-GHZ，512-MB，Pentium IV，Windows XP Professional。 这里我们看到的任何结果揭示了在其它环境下可能的性能，这不说明可以假设这些结果就是正确的典型例子。 最后要说明的是，当我提到“C#比Java好”等结论的时候，一定是指“在特定的测试环境的条件下”，这一点请你铭记在心。 </P>
<P><STRONG>性能评价 </STRONG></P>
<P>所有的代码还是用和第一部分一样的编译器编译。 C#代码在.NET框架 1.0.3705（可从http://microsoft.com/netframework/免费下载）下，由Visual C#.NET 编译器v7.00.9466编译。D代码由Digital Mars D 编译器 Alpha v0.62 (可从http://www.digitalmars.com/免费下载) 编译。Java代码由J2DKSE1.4.1-02 ( 可免费下载 http://java.sun.com/ ) 编译。 C和C++代码 ( 单线程 , 包括 <STRONG>raii </STRONG>, 和静态链接库 ) 由使用 STLPort v4.5的Digital Mars C/C++ v8.33 ( 可从 http://www.digitalmars.com/免费下载 ) 和使用 Visual C++ 6.0的头文件和库文件的Intel C/C++ v7.0来编译。所有这些编译器均使用最优化处理 ( set for speed ) ; Intel 用-QaxW flag [2]提供两套编码路径：一个是针对有SSE2的Pentium IV处理器，另一个是针对其他所有的处理器——在运行时选择合适的一个。（因为Intel编译器只适用于Intel处理器构造，特别是在这个测试中的Pentium IV，这编译器的编译结果必须在此基础上评价，不能在其他的处理器上进行同样的过程，比如Athlon，甚至是旧版本的Intel处理器也不行。） </P>
<P>后面将要描述我们测试的假想的五种例子中的性能表现。他们都用2-GHZ，512-MB，Pentium IV，Windows XP Professional的配置，并沿用在第一部分用的性能分析系统。每一种语言所编程序的调试都执行7次，同时没有其他占线过程，去掉最高速的和最低速的，纪录平均值。所有的时间信息，通过仅区分相应时间区间的高性能计时器保存下来。（C用户调用Win32 API <STRONG>QueryPerformanceCounter() </STRONG>；C++ 用 Win-STL ' s <STRONG>performance_counter </STRONG>[4], 可在http://winstl.org/找到 ； C# 用 <STRONG>SynSoft.Performance.PerformanceCounter </STRONG>，可在 http://synsoft.org/dotnet.html找到； D 用 <STRONG>synsoft.win32.perf.PerformanceCounter </STRONG>, 可在http://synsoft.org/d.html找到；Java 用 <STRONG>System.currentTimeMillis() </STRONG>.）每一个测试程序都包含一个启动迭代的程序，这样可以将启动或缓冲时的影响最小化。 </P>
<P>在每一个例子中的C#，C++和D的特征: C在所有 保存 封装和 <STRONG>raii( </STRONG>类)的地方出现，因为它没有这两种例子必需的语言特征。 <STRONG>raii </STRONG>没有出现Java，因为Java甚至连 Resource AcquisitionIs Initialization (RAII [1])的机制也没有，所以它仅仅作为在GC-kicking的一个练习。简单的说，我不会展示所有的语言的每一个例子的所有特性，但是所有这些都包含于在线记录中（连同产生他们的Digital Mars make-compatible make-file）。 </P>
<P>下面就对测试性能的例子进行评价 : </P>
<P><IMG height=306 src="http://mag.vchelp.net/200312/images/part2/image001.jpg" width=553><BR><STRONG>封装 </STRONG>。这个例子（ Listing1）的目的是比较不同语言（除了C）在类机制上的代价，这些语言并不完全支持相对于它们所做的所有类型的一般处理。尤其是C#和Java一般不能存储和使用它们的内置类型实例，比如int，long，bool等等，同样，对于从这些实例派生的类型也不行。相反C++和D都支持模板，它可以给任何类型的一般性操作带来方便。在以后的文章里，我将详细地讲解容器。但在这个章节将不会用到它，如果提及它反而会分散对于封装意义的理解。C#和Java中未封装的变量传递ints给异或函数，封装变量传递整形值在Int32（C#）和Interger（Java）的实例中。注意Java不支持模糊的封装（这被证明是好的因为编译器将对于这种封装带来的低效给予警告），所以实例必须明确地被创建（在列表1中标记“Java ” 处可以看到）。 C++和D的运行是简单的使用模板，这就是它们的泛型机制。（在这里我不对“一切都是对象”的模板的好处作深入探讨，但是我将在这期的姊妹篇Stepanov camp中对它做详细讲解）。 </P>
<P><STRONG>异常处理 </STRONG>。异常的语义在各种语言之间有很大的区别。 在 C++，异常处理和调用析构函数一起沿着堆栈操作下来，然而在有垃圾回收的语言中，这些对象被丢弃，直到将来的某一时刻被收集。 这样，就无法对各种语言进行有意义的比较。所以在这个例子中，只能通过将一段 using/aware代码与另一段没有异常处理支持的相同代码进行比较。这个例子有三种测试，它们的目的就是去衡量使用和抛出异常处理的代价。第一（Listing 2）是比较使用异常处理和通过函数返回值来传递信息给调用者这两者的代价。第二（Listing 3）是评估在try-catch模块中执行的代价（尽管没有异常处理被抛出）。最后（Listing 4）就是评估遍历执行try-catch模块的代价（同样，这没有异常处理被抛出）。 </P>
<P><IMG height=362 src="http://mag.vchelp.net/200312/images/part2/image002.jpg" width=554><BR></P>
<P><IMG height=549 src="http://mag.vchelp.net/200312/images/part2/image003.jpg" width=381><BR></P>
<P><IMG height=270 src="http://mag.vchelp.net/200312/images/part2/image004.jpg" width=388><BR></P>
<P><STRONG>Mstress。 </STRONG>这一个例子的目的是，在假设分配比回收优越的基础上，看看垃圾回收语言是否在高的内存利用率上有重要的非线形关系。这里存在四种测试，每一个都牵涉到分配与释放（ C 和 C++）或者丢弃（C#, D, 和 Java）在一个时间段内的内存块。这些块大小是通过相同的伪随机数算法生成的（详见文章的第一部分），这能使块的大小处于1至1000之间，从而在确保每种语言在相同的环境下测试时仿效现实世界中的分配。第一种测试（Listing 5）通过一个不断循环的变量分配了1000个内存块，目的是看看频繁地分配会不会“留下”更多的碎片，这些碎片会阻碍分配的进行直到突然产生明显的GC收集。第二种测试（Listing 5）增加了1000次循环中分配的次数，以测试由不断增加的内存负担引起的性能的 损失 。 第三和第四种测试（没列出来）跟前两个相似，它们增加了另外的内存块和分配/回收的交叉活动，目的是为了模拟不断增加的内存碎片。 C使用malloc()/free()，而C++，C#，D和JAVA用new[]。 </P>
<P><STRONG></STRONG></P>
<P><IMG height=268 src="http://mag.vchelp.net/200312/images/part2/image005.jpg" width=554><BR><STRONG>rafile </STRONG>。这一个例子（ Listing 6）评估各种语言从大文件中随机读取的能力。它利用某种伪随机算法（详见文章第一部分）来模拟在大文件中的查找/读取。这里用到了两个文件，一个4.2MB，一个21.1MB。在每一个查找点，四个字节被读取，并且和一个运行值进行异或运算（这样有利于证明所有的语言以相同的方式查找和读取）。有意思的是，原始的运行程序读了32位的整数，但不幸的是，JAVA用了network byte-order而不是host byte-order，这与C, C++, 和D的machine-order reading是不相容的。 </P>
<P><IMG height=240 src="http://mag.vchelp.net/200312/images/part2/image006.jpg" width=554><BR></P>
<P><STRONG>Raii（类） </STRONG>： 这最后的例子目的是为了评估没有内建支持这个非常重要的语言特征（很不幸 C#和Java都没有）而付出的代价。此例子表达了通过资源句柄XUser 类型从XFactory资源管理器分配X资源到客户端代码(见代码段7)。在C++和D语言中，XUser 提供一个析构函数，在析构函数里分配的资源自动地回收到资源库，资源在这种情况下是个信号量，.NET和D的执行代码可从 <A href="http://synsoft.org/dotnet.html">http://synsoft.org/dotnet.html </A>和 <A href="http://synsoft.org/d.html">http://synsoft.org/d.html </A>下载。C#、D和Java 提供它们自己抽象的同步机制，但我需要一个信号量去引起交叉线程的竞争，去保证同样的同步机制以便所有语言的进行生动的比较。此信号量在每种语言执行代码中都有最初可获得的五个键值。 </P>
<P><IMG height=716 src="http://mag.vchelp.net/200312/images/part2/image007.jpg" width=553><BR></P>
<P>尽管 C#的XUser 类有一个析构函数，但此析构函数仅在实例进行垃圾碎片收集时被调用。（C#使用析构函数的语法，但编译器把它翻译成 <STRONG>CLR </STRONG>（公共语言运行时刻）的 <STRONG>Finalize() </STRONG>方法。）这种情况跟 Java一样。C# 还尽量更多地支持习惯用语（虽然它支持的还不够），这是通过使用引用声明来实现的，正如表7所示。（为了方便，“using-ed”类型必须实现 <STRONG>IDisposable </STRONG>接口，否则声明无效），一个明显的问题是类的使用者必须承担确保语义的正确的负担，而不是类的开发者。这是奇怪的二分法：C#是面向对象的，但它依靠类的使用者对类的内部执行代码的足够理解以决定是否需要此类的直接析构。C++和D语言过程明确，由类自己决定：C++通过析构函数的执行，D提供析构或终止函数，类声明验证关键字 <STRONG>AUTO </STRONG>（没有这些，它按C#和Java的方式运行正常的垃圾碎片收集程序）。 </P>
<P>C#语言使用 <STRONG>USING </STRONG>声明（和 <STRONG>IDisposable </STRONG>接口）的一个可替代方法是依靠垃圾回收。在形成这个情况之前，我假设当内存使用率达到触发阀值或者主线程空闲/等待的时候启动垃圾碎片回收。（我不知道这种误解是怎么形成的，或许只是异想天开）。当我在执行 <STRONG>Raii2 </STRONG>例子的最初版本的时候（变量没有使用 <STRONG>USING </STRONG>声明），处理进程马上挂起。很明显，在其他线程堵塞时垃圾碎片收集没有触动，只有在内存消耗到一定程度才触动。因此，为了程序执行，我们被迫在另一个工作线程中自己剔除垃圾碎片收集。当然，这在实际编程中是非常可笑的执行策略，执行结果证明了这一点。 <BR><BR></P>
<P><STRONG>结论 </STRONG></P>
<P>大部分的结果是以绝对运行时间数（全部循环或每循环）或者 C#的执行时间和C，C++，D，Java分别的百分比来表达的。在后一种方式，围绕着百分刻度的结果表示C#的性能比率，高的值表示C#的性能优越，低的值表示相对低下。 <STRONG>Raii </STRONG>的情况是个例外，在这里结果是以C++（Digital Mars）时间的百分比表示的，由于垃圾碎片收集代码的消耗，调用时间值是以对数刻度表示的。这说明在对数比例上归根于非常高的垃圾处理花费. </P>
<P><IMG height=332 src="http://mag.vchelp.net/200312/images/part2/image008.jpg" width=502></P>
<P>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD width=41></TD></TR>
<TR>
<TD></TD>
<TD><IMG height=288 src="http://mag.vchelp.net/200312/images/part2/image009.jpg" width=422></TD></TR></TBODY></TABLE><BR><STRONG>BOX </STRONG>. 从图 1中我们能看出,在合理范围里(C#语言的97%-271%)对于所有编译语言解除封装操作(就简单整形数据)成本的差别：最差的是C++(Digital Mars)，成本是C#的271%,最好的是C++(Intel) 成本是C#的97%.假设执行相对比较简单的 <STRONG>异或 </STRONG>运算, C#几乎可以像 Intel那样出色，你对这个并不会感到很惊奇，但你相信C＃要比C++(Digital Mars)和D模板实例快两倍.我有些惊讶C++(Digital Mars)这个相对弱的性能,特别是和D比起来它要慢得多，然而他们出自同一开发商。 
<P></P>
<P>显然这个封装花费是有意义的 ,相当于C++模板花费4-10倍.这个测试仅仅包括整数,相对来说可能不能完全反映特殊类型.虽然如此,我们能有把握断言这个模板轻易赢得了性能战.有趣的是就他们各自的成熟度而言，C#要比java稍好, </P>
<P>差距并不大 (小于4%).(我想可能由于.NET处理目标JIT3。【3】)。更有趣的是,事实上在未封装的形态下,c#明显比java快. </P>
<P><IMG height=306 src="http://mag.vchelp.net/200312/images/part2/image010.jpg" width=424><BR></P>
<P>除了图 1以外,有人认为,不抛出的异常variants都是是比抛出的异常副本要快的(如图2). 用 c和c++,Intel完成任务要比所有其它不抛出的异常variants快.c#紧随其后。似乎c,c++,D(不考虑编译器)在异常处理上花费大致相等,符合win32异常处理机制的预期限制因素.我们能看到三种语言在抛出异常variants上的差别是比较小的,很可能的原因是由于有相关的额外的管理消耗关系 (c++规定执行破坏功能的堆栈是从捕获点到抛出点的来回). </P>
<P>我发现有趣的是在抛出异常variants方面c#和java的执行是有关系的. </P>
<P>因为这两种语言语义在异常抛出处理要胜于返回当前错误值 ,也胜于其它语言, </P>
<P>尤其 c语言所呈现的关系执行调用.我非常赞同Brian Kernighan和Rob Pike </P>
<P>(<>,Addison Wesley,1999)所说的例外仅仅在例外条件的使用,并不是必然会发生的.然而人们能容易看到为何服务器在处理巨大数量的数据的时候可能出现的例外,而经常担心性能的问题.正是如此,java以小于其它语言25%的消耗给人深刻的印象.由此我们限定win32,采用两个有代表性的差不多的c编译器来构造例外处理,虽然没有定论,但是我认为java使用了不同的机制. </P>
<P>那么c#的异常抛出性能如何呢?在相关条件下,相对其他语言而言，使用异常处理语句是不使用异常处理语句的21-77倍，而c#则是191倍;鉴于Java的出色表现，.net工具应该引起注意了。 </P>
<P><STRONG>Except-2. </STRONG>这个图表是为了弄清楚在一个 try-catch范围内异常处理语句的执行是否耗时,如图3,大多数语言的表现同预期的一样:c、c++和D对于Digital Mars是类似的;Intel c 和c++比其它都好.c#要比java快.相比较就性能而言, </P>
<P>c#是很优势的,令人感兴趣的是，c#在进行异常处理是耗时比不进行时还要少.产生这种结果肯定是有原因的(这个结果是经过了多次验证的), 但由于对于c#异常处理机制我没有很深的认识，我不能做出合理的解释.同样,虽然并没有定论,对于D来说，是否进行异常处理对结果似乎并无影响！. <IMG height=375 src="http://mag.vchelp.net/200312/images/part2/image011.jpg" width=508><BR>我认为这是因为它不用清除栈内数据， java也有大致相同的自由度，并且表现出的差异也并不大。 </P>
<P><IMG height=375 src="http://mag.vchelp.net/200312/images/part2/image012.jpg" width=511><BR></P>
<P><STRONG>except-3 </STRONG>如图表 4所示,测试结果与预期大体相符。除了D编译器的结果中微不足道的差异，所有的语言在遍历异常处理语句的时候都比不进行是耗时要长，尽管如此，我仍然对在这两种情况下差异如此之小感到吃惊。我们通常认为使用异常处理机制会耗费时间，并且我认为遍历异常处理语句会在启动和完成上耗费大量时间；事实上，我一开始就预期两者的差异将会非常地显著。然而，在这个测试当中，两者的差异相当不明显。 </P>
<P>但是这个结果并不难理解，导致差异如此小的原因首先在于：这次测试太过于纯粹。这次测试的所有异常处理（类型）都是 int型的。尽管并没有异常被抛出，这样做的目的在于避免在异常处理体的构造（和析构）上耗费时间和消除各编译器处理机制不同所造成的影响。其次，本次测试中没有基于框架的结构体，因为在C++中，尽管析构函数并不会真正被调用，析构函数调用方面的准备也是必须进行的，这样也会造成时间的耗费。（注意析构函数无论异常是否被抛出都会被调用。）这些耗费都会造成测试的不公平，所以我们选择了int型。注意到程序不论是因为异常抛出而中止还是正常退出，析构函数都是要被调用的。所以它仅仅做为勾子而被添加进这些额外的函数调用中。然而，我认为所有的这些因素并不是很充足，它们仅仅使我们可以从那张表里知道当不使用的时候异常的开销是非常小的。很自然，这也正确的表明了一个人对所有语言性能(perform)的期望，当然我们要给它足够的信任. </P>
<P><STRONG>metress-1 </STRONG>这个测试很清楚的表明了在不使用内存分配机制的展示 (exhibit)中不断增长的对固定数量的内存快（随机大小）分配/释放的循环的反应是非线性的。事实上，在很大程度上它们从没有线性(增长)的趋势:从性能上来说并没有什么变化(见图5),除了在一些低循环(low interations)上有所不同之外.(有趣的是，那些低循环的非线性是有意义的--占到了全部命令的50%还多--当然这仅仅是对c#和java而言).不管内存分配机制是否在每次特循环结束后都立即恢复对1000个内存块的释放,或者仅是简单的把它们交给GC在以后的某个时间处理,语言/运行库总是看上去几乎完全不受这些循环执行的影响. </P>
<P>在这组性能相对的测试中，我们可以清楚的看到一些它们之间的不同。在使用 Digital Mars 分配方式的语言中，C和C++的表现是最好的。Visual C++的运行库比使用Digital Mars的语言低大概2.5-3倍，对于使用Digital Mars 的语言和Visaul C++运行库来说，C和C++基本上是相同的。最后,很明显，Java 比C#慢了3-4倍，而比C慢了差不多7倍. </P>
<P><IMG height=393 src="http://mag.vchelp.net/200312/images/part2/image013.jpg" width=514><BR></P>
<P><STRONG>mestress -2 </STRONG></P>
<P>就像我们从图表中看到的，在一定数目的分配释放内存的循环中内存块（随机大小）的不断增长的反应是变量的引用是非线性表现的。这正是我们所希望的，因为在每次循环中分配的内存总量是按指数方式增长.每次循环所分配的内存的大小都低于10，000块.使用Digital Mars分配方式的C 和C++的效率依然是极为优秀的。只是效率表现上低于平均值，而这里也不得不提到java，它的表现同样不好.Visual C++的运行库（C和C++都适用）相对于C#，D 和Java来说，有一个不错的开始，但它很快就降到了一个很低的水平上。 </P>
<P>Java在每次循环是使用10，000内存块之前的表现非常有竞争力，并在这一点急剧上升。D在整个测试中都几乎有着一致表现.几乎一直都是非线性的，这与C#很接近。 </P>
<P>如果可能的话，我都希望看到一条总够长的X轴，C#在内存溢出的情况下仍然保持每次循环不超出10，000内存块的水平并预防这种情况的出现。（D和Java似乎也做的到，但那也只是类似C#的行为一旦被发现就中止测试。) </P>
<P><STRONG>mstress-3 </STRONG>和 4 从前两个测试(variants)看,除了时间的的增长，反复的交叉存取和块变量在执行上的表现并没有什么不同。曲线的走势或者说不同的语言/运行库的相对表现在这一点上没有什么明显的改变。 </P>
<P>我认为C和C++，在使用外部free/delete的条件下，重新使用的新近释放的碎片。相反的，我很难想像出C#,D和Java 是如何使用垃圾收集机制周期性寻找每次循环所分配的内存从而尽可能的减少由内存碎片所引起的负面效应的,或者在这个跟本就没有产生碎片.除去这些不同，这两种方式的表现还是很相似的。 </P>
<P>这只是一种理想的我们所希望的分配机制的表现 ---毕竟，那是一个很极端情况，所有内存分配都可以在给定的周期内完全返回---虽然程序的执行都达到的目的。 </P>
<P><STRONG>rafile. </STRONG>这次测试中我所希望是那些语言实际 (效率)上并没有什么不同.除了C++的执行也许比其它的低上几个百分点. </P>
<P><IMG height=376 src="http://mag.vchelp.net/200312/images/part2/image014.jpg" width=515><BR>图表 7中可以看出，C#的在文件的随机存取上比C(使用Digital Mars)要好,但低于C++(使用Intel和VC6的连接库),和D与Java现表现基本持平。但是C++运行库表现出令印象深刻的性能。 </P>
<P>从所有这一系列测试来看，Intel 似乎已经能够产生性能不错的代码了。但是它的连接的运行库头文件却是Visual C++ 6.0的，这个（大概）不是由Intel编译器产生的。因比它几乎是以压倒性的性能优势超过了DMC，这主要是由于各自的Digital Mars 和微软Visual C++运行库的性能。(我承认有点吃惊，对于些测试结果正好可以反对两个卖家的名声--应该或是不应该得到的)。这也表明一个人的偏见是非常没有理由的。 </P>
<P>另一件值的注意的事就是对不同大小文件访问的开销都非常的小。这也表明所有的语言都利用操作系统的优势很轻易的达到了相同的程度。 </P>
<P><IMG height=400 src="http://mag.vchelp.net/200312/images/part2/image015.jpg" width=529><BR></P>
<P><STRONG>raII </STRONG>. 从图 8中我们能看出使用statement的C#的表现只有C++析构函数效果的一半。D的性能与之相当，虽然产生的代码中有错误（或者是D的Phobos运行库),当进程创建的对像超过32，000左右的时候会使该进程挂起，从而防止过多（一个以上的）通讯中的数据点被确定的使用.在这组单独的测试中我们可以看到RAII对C#和D的支持是很完善的，但并不如想象中那样优秀。如有你要做很多scoping--并且你想，也许要足够的robustaness的帮助---你最好还是选择（stay with）C++。当依赖于.NET的垃圾回收机制时，因为处理过程只是简单地被挂起，所以C＃的性能比较难以测算。可是我们不希望.NET经常调用它的垃圾回收线程，但我找不到理由解释为什么回收线程不以较低优先级在内核中等待，当有垃圾对象需要被回收和主线程空闲时再进行有效的回收。当然，我在垃圾回收方面不是专家，并且可能有一种合理的理论原因说明这为什么不是一个好的处理方法。 </P>
<P>GC（垃圾回收）即使响应的更灵敏，我们可以在它的性能结果上看出效果并不与之匹配。对应第二组数据指针的方法表明了GC每隔1ms被触发一次。 </P>
<P>GC（垃圾回收）的方法比使用使用声明慢了近千倍，所以这不仅仅是一个技术问题的范围。自然，这跟关于这个例子的假设有关，同时跟几乎每个人都用垃圾回收机制去管理频繁争用的资源的释放这个不辩的事实有关，但我没预计到这个变量的性能是如此之差。同时这也给了读过Java/.NET书籍的C++程序员对于书籍建议（或许说依靠）使用终止函数来清除资源而感觉疑惑的一个解释。 </P>
<P><STRONG>结论 </STRONG></P>
<P>在第一篇文章，我对于不同的环节得出不同的结论，这些环节或是语言本身造成的，或是库或者二者造成的，在这里我也这么区分，因为语言特征而产生的影响的部分通常涉及：异常，封装 /模板和RAII的实现。文件存取部分可以看作直接对库函数的操作。（没有任何一种语言阻止写替换操作，虽然这样做并不是很容易）。内存管理部分受语言和内存的影响――虽然我不清楚可以用哪一种管理机制去替代C#和JAVA的缺省的内存管理机制，但是对于其他语言这是很简单的。 </P>
<P>封装。当我们比较封装和模板时，我们可以看出模板明显比封装出色很多。我相信这不会让大家感到很惊奇，但是事实上封装消耗的系统资源是模板的十倍。自然，作为模板类库的作者（ <A href="http://stlsoft.org/">http://stlsoft.org/ </A>），我得出这个结论也许带有偏见，但是这些数据在表示更广泛的情况时，他们自身就说明这一点。在后面，我将说到容器和算法，我们将看到比预计更多的这样的结果。 </P>
<P><STRONG>至于有关异常情况，我想讲四点： </STRONG></P>
<P>1.在所有的程序语言中，使用异常处理从调用的函数过程中返回，而不是保留他们作为异常事件的指示，将导致执行花费巨大的成本。 </P>
<P>2.C#的异常处理机制相对于其他被测试的语言效率是极低的. </P>
<P>3.在有异常处理的上下文环境下运行（比如用try-catch的情况）对于性能没有多大影响，除了C#，它为了提高性能（实际上，我对于这种结果很不理解，并且怀疑这是.NET运行期的一个人为结果而不是C＃/.NET的一般特征。） </P>
<P>4.交叉于异常上下文的执行（比如说进入try-catch和/或 离开try-catch）对于系统性能的影响基本上是非常小的。 </P>
<P><STRONG>Raii. </STRONG>C#支持的RAII例子比C++在性能方面差，虽然不是很多，但与D相比差别无几，基本一致。（这种一致指出了在处理堆分配对象的确定性析构时的基本限制）然而，从理论观点，或从易用性和/或稳健性的实践观点来看，这里还是有很大差距的。C语言缺乏机制可以解释为年代已久，并且它是一种程序语言。很遗憾，java缺乏这种机制，但是它只是可以解释为忽视了。（至今为止我们已经用java8年左右了，所以“忽视”可能也有些牵强。）C#在这方面的努力还不成熟（因为它更多地依赖类的用户而不是类的作者），很奇怪的是C#/.NET很多优点都是在Java中被看作瑕疵/遗漏的地方，比如属性，输出参数，和无符型。 </P>
<P><STRONG>Mstress. </STRONG>这个内存测试的目的是证明如果频繁的内存分配加上垃圾回收机制是否会导致—— C#, D, 和Java这些包含垃圾回收的语言严重的非线形，这明显没有。这个结果可以看出所有的语言/库表现的非常合理的。这里我们可得出几个有趣的结论： </P>
<P>1.C语言和C++语言，提供正确的库支持，他们内存分配空间最快。毫无疑问，部分原因由于它们没有初始化字符数组的内容。根据结果，我重新对C语言进行测试，用calloc()替代malloc()，测试结果很接近C#，虽然仍然会高出5个百分点。 </P>
<P>2.内存碎片（只要轮流存取第三和第四个变量）不会在很大程度上影响内存分配的性能，不会增加总体负担。 </P>
<P>3.如果垃圾回收器安排在没有使用的内存区之后不执行（我假设这是可以的），这将不会对性能有太大的影响。我假设，这说明了C#是第一个会内存枯竭的语言，所以我们可以假定它们通过使用大量的内存空间在内存分配性能方便取得平衡，并且相信在现实的环境中有这种机会运行垃圾回收。 </P>
<P>4.通常更希望体面地降低性能―就象C（Digital Mars）的方式――而不是在各个方面都有很强大的性能，然后在某些未知的阀值化为乌有：在服务器环境下，某一时刻提供一个慢点的服务比导致崩溃可能要好。由于对Digital Mars和Visual C＋＋，C和C＋＋的运行实际上是相同的，我们可以假定因为与通过new操作符的调用的交流而增加的成本可以忽略，并且在C和C++之间没有本质的区别。 </P>
<P>5.C#内存分配时间会比Java快上3～4倍。 </P>
<P>总的来说，这个结果并不象我想象的那样：我期望C和C++在中小应用比 C#，D，和Java稍微落后，但在大型应用中远远优于。真是学无止境。 </P>
<P><STRONG></STRONG></P>
<P><STRONG>Rafile </STRONG>. 对于文件的随机存储结果可以看出一些重点。我认为最重要的一点是仔细的选择库。之所以C++的运行效果看上去要比C和其他的语言好很多，就是因为库的原因。当把C＋＋的性能（Intel 和VC6库）和其他语言/编译器进行比较时，用其他任何一种都很给人留下深刻的印象。自然，这边的例子是故意的，但这么大的性能差别的事实——比C#和 Java快23倍，我们可以期待在现实情况中性能的有意义的差别。（这里，再次证明了偏见是错误的：我很厌恶所有的io流，从很多方面很容易能看出它的效率很低，当然也不能说全部这样。我对于这样的性能印象很深刻）。 </P>
<P>详细的总结了C#的性能以后，我想就我们在其他语言方面的研究发现做一个简短的介绍。 </P>
<P>1.C处理异常事件是不错的，特别是在存储和文件（提供正确的库连接）有非常好的表现；它能提供所有我们能预期的效果，但是与后来的高级语言相比，吸引不了更多的学习者。 </P>
<P>2.C++处理异常事件也不错，特别是在存储和文件（提供正确的库连接）有非常好的表现，而且包含模块的概念和有效的RAII；它始终得到我的钟爱，我认为它在未来的很长一段时间内都是编程的最佳选择。 </P>
<P>3.D在异常事件处理上不是很太好，在文件处理上处于一般水平，在存储方面有比较合理的相关性能和非常不错的线性度，有模块和RAII（虽然语言本身有很多的bug产生，使得很多的处理过程被挂起）；当这语言经过它的初级阶段，期望会得到不错的效果。 </P>
<P>4.Java在封装和内存管理的性能上表现最差，不过它有好的异常事件处理，但没有一点模块和RAII的概念；它不指望会达到真正的美好，我认为C#/.NET可以衡量出它的高低，至少运行在Windows平台上 。 </P>
<P><STRONG></STRONG></P>
<P><STRONG>摘要 </STRONG></P>
<P>从第一部分开始，就给出全文的延伸的意思，从性能方面，如何能更好的用C#/.NET写出很好的软件。在第一部分里，一些结果虽然不是很值得注意，但是有很多的地方还是令人叫奇的（至少我是这样的！）。 </P>
<P>这些研究结果展示了C#可以提供非常好的性能效果——至少在我们测试的范围内是这样——同时不能把它如同象过去把Visual Basic和一些扩展版本的Java当作一种性能差的语言来对待。对我而言，我比较注意语言运行的性能，当然，他们的表现也会超出我的预期效果。我所提出的严肃评论是从语言特征的观点和对多个编程范例的支持方面得出的。 </P>
<P>作为一个最初使用C++的程序员，我从特定的观点得出这些比较结果。这可以从RAII和封装测试以及它们的语言解释上看的出来。在某种意义上来说，这就好比比较苹果和橙子，对于不同背景的人们可能会对我为什么如此强调模板和确定性析构感到疑惑，没有这些他们也进行的很好。 </P>
<P>毫无疑问，模板给C++带来了非凡的革命，它能支持群组合作，或者独立运行，范例是无可比拟的。Java由于缺少模板和强制要求任何东西都是对象，而被批评了很长时间。.NET框架在这方面做法一样也很让人失望；可能由于Java缺少模板而他们可以在.NET环境下得到（他们确实达到）让更广的开发团体感到信赖并接受了它。 </P>
<P>缺少对RAII的支持对GUIs（通用用户接口）是非常好的，这是在严格的框架内操作的软件——即使象J2EE这样精密复杂和高吞吐量的语言，和非关键的程序。但复杂的软件不必要因为它而复杂。在最好的情况下，到处都是最终模块，只要在Finalize()里函数里添加Close()方法就可以了。在最坏的情况下，滞缓或者随机的资源泄漏都会导致系统的崩溃。更甚于以上的，如果OO（面向对象）——就象C#和Java所有都是对象——是你的目的那更让我不安，第一个要失去的就是对象自己清除自己的责任，我没办法理解这个。（我知道一个非常出名的C++程序员——当然我不能告诉是谁，他告诉我当他在课程中突出RAII和模板的重点时，那些非使用C++的人们露出的滑稽表情，让他感觉好象他丢失了什么东西） </P>
<P>D是使用垃圾回收的语言，默认是非确定性析构,有很多地方与C#和Java相似，但是尽管如此，它还是兼容支持RAII的习惯同时具有模板。并且它是一个人写的！我不明白为什么我们对Java或者C#（.NET其它语言也是一样）印象如此深刻，即使有它们支持RAII和模板的缺点，而我们又能比较这些。有可能C#/.NET成功的原因和Java一样，有大量的，有用的库文件（C和C++应该从中学习，D应该去发展），和有一个强有力的后台。 </P>
<P>最后，我想说对于所有的性能结果的比较分析，你必须明智地使用这些结果。我努力使自己公平，选择我相信是公平和有意义的测试。但你一定要判断的出这些例子仅代表了广大可能性的的一小部分（不是无限意义上的），它们不是实际的程序，仅仅是测试而已并且把它简化了，其中不可避免的带有我个人的某种偏见在里面。通过逐步的方法，我希望降低这些因素。但你在阅读和使用这些结果的时候要保持留意。 </P>
<P><STRONG>感谢 </STRONG></P>
<P>我要感谢 Walter Bright提供给我最新的Dv0.62版本，能够完全的测试异常事件。感谢Intel的David Hackson给我提供了C/C++编译器。还要感谢Scott Patterson帮我选择了切合实际的测试方法（他总是不断的在我烦躁、偏题、愚蠢的时候提醒我）。还要感谢Eugene Gershnik 对于我的一些断言给了我严厉的反驳，帮助我适当地注意一些防止误解的说明。 </P>
<H1>Notes and References </H1>
<P>[1] The C++ Programming Language, Bjarne Stroustrup, Addison Wesley, 1997 </P>
<P>[2] The Software Optimization Cookbook, Richard Gerber, Intel Press, 2002 </P>
<P>[3] Applied Microsoft .NET Framework Programming 1 st Edition, Jeffrey Richter, Microsoft Press, 2002 </P>
<P>[4] Described in “ Win32 Performance Measurement Options, ” Matthew Wilson, Windows Developer Network, Volume 2 Number 5, May 2003. </P>]]&gt;<img src ="http://www.blogjava.net/qq13367612/aggbug/16242.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-15 10:24 <a href="http://www.blogjava.net/qq13367612/articles/16242.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C# 与 C 、 C++ 、 D 、 Java 的性能比较[第一部分]</title><link>http://www.blogjava.net/qq13367612/articles/16243.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 15 Sep 2005 02:23:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16243.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16243.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16243.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16243.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16243.html</trackback:ping><description><![CDATA[<P>C# 性能能赶上编译型的 C/C++/D 和中间代码运行时解释的 java 吗？ </P>
<P>微软发布了 .net 平台和 .net 平台支持的多种语言，包括 C#,vb.net, 受管 (managed extensions) 的 C++. 它们构成的体系能同时满足开发出高效的服务器架构，和开发部门对强壮的多功能的接口的需求． </P>
<P>和其他众多程序员一样，拿新开发语言与日常用到的语言作比较和选择的时候，我对新玩意总有莫名的偏见和怀疑．本篇文章将会讨论 .net 平台的优点，特别是相关的 C#. 我们拿 C# 和其他开发语言在高性能软件开发方面，作定量的性能比较． </P>
<P>因为 c# 程序编译运行分为两部分：先编译成中间代码，然后是中间代码的运行时解释执行，所以我们不仅会把他跟纯编译型的 C/C++/D 语言相比，也会把他与典型的 ( 中间代码运行时解释 ) 的 Java 语言作比较． Java 无疑是一个成熟的语言，应用在各个计算机领域． Java 程序先被编译成中间代码，然后运行时通过 java 虚拟机解释成本地机器码执行．由此看来， .net 平台，特别是为首的 c# 语言，借鉴了 java 的很多东西，包括语法和运行时支持． </P>
<P>对新语言（或者说时语言 + 对应的库 + 该语言的运行时支持）作完整公平的性能比较算是一个庞大考验．本文对 c# 与 C/C++/D 就开发中都涉及到的基本共通点作初步的性能比较，同时还包括各个语言所带的库的性能比较．包括： </P>
<P>1. 运行一次完整的程序（转载执行一个程序的时间） </P>
<P>2. 算法和递归（计算圆周率和 Eratorshnes's sieve ） </P>
<P>3. 类型转换（ int->float,float->int,int->string.string->int ） </P>
<P>4. 字符串比较 </P>
<P>5. 字符串连接 </P>
<P>6.( 字符串特定符号统计 ) </P>
<P>通过上面的一系列比较，将会揭示 c# 编译器效力的一些有意思的地方和 .net 运行库的性能 </P>
<P><STRONG>背景 </STRONG><STRONG>: </STRONG></P>
<P>虽然作比较的语言都可以算是 C 语言系列，虽然这五种语言在语法上只有或多或少的不同，但在其他方面，它们的差别是巨大的．（见表一， http://www.digitalmars.com/d/comparison.html ） . 我假设读者已经熟悉 C/C++/Java ，以及一些基本的 C# ．对于 D 语言，不如其他语言那么有名，但你可以找到它的介绍 </P>
<P><STRONG>D </STRONG><STRONG>语言介绍 </STRONG></P>
<P>D 语言是 Walter Bright 正在开发的一种语言，安装和学习这种语言的开销很小。实质上， D 语言的目标是更成功的 C/C++ 语言，改进包括可变长度的数组，函数参数的 [in][out] 属性，自动内联，版本控制，局部函数，内建单元测试，严格的类型检查等等，包括 Java/C# 中实现的忽略指针和引用的差别 , 这在 C++ 中只有通过模板实现。它同时支持对现有 C 函数和 API 的直接支持。 </P>
<P>它支持许多跟 Java/C# 类似的特性 , 比如垃圾收集。 D 语言跟 C#/Java 的本质区别是 D 语言运行时不需要运行时的虚拟机支持。 D 语言所支持的所有特性都是在编译连接时完成的，所以不需要运行时的额外支持。 D 语言的开发目前已经到了 Alpha 版本，将来的成熟版本在绝大多数情况下将会比 Java/C# 高效得多，除非 JIT 优化非常明显．所以我们才把 D 语言加到我们的测试当中。实际上， Walter 告诉我们， D 完全有能力产生跟 C 代码运行得一样快的代码，同时，由于自动内联，内建数组，字符串内建优化，非 \0 的字符串结束标记等特性，他相信在 D 语言正式发布的时候，它的性能将会超过 C 。 </P>
<P>在真正的测试开始以前，我假定性能按照从高到低排序，应该是 C/C++,D,C#,Java ．我跟其随后证实的其他好多人一样，一开始就怀疑 C# 产生高性能代码的能力． </P>
<P><STRONG>性能比较 </STRONG></P>
<P>我们会在下面接着介绍的十一种不同情况下测试性能．测试平台是 2G 512M 的 pentium4 ，操作系统是 Windows XP Pro. 测试是在一套我自己设计的工具下进行的，为了符合这篇文章，我理所当然地用 C# 写了这个工具。在没有其他干扰进程的情况下，对每个测试者运行七次，分别去掉最快的一次和最慢的一次后得到的品均值，就是测试者的成绩。除了下面说到的 noop 测试，所有的测试通过高精确度定时器计算完成所有内循环所需时间完成。（译者注：比如 int->float 测量中，会通过循环运行多个 int->float 的转换，然后测量完成整个循环需要的时间）。如 Listing1 所示的代码，就是用 c# 完成的 f2i 测试。 (C 通过调用 win32 api 函数 QueryPerformanceCounter() ； C++ 通过 WinSTL 的 performance_counter;C# 通过 SynSoft.Performance.PerformanceCounter;D 通过 syncsoft.win32.perf.PerfomanceCounte ； Java 通过 System.currentTimeMillis()) 。每个程序都包含进入实质测试代码前一段 ” 热身代码 ” 来减少程序初始化 ,catch 等因素带来的不利影响。 </P>
<P>编译器版本号如下： </P>
<P>C#:VC#.net v7.00.9466,.NET Framework v1.0.3705; </P>
<P>D: Digital Mars D compiler Alpha v0.61 </P>
<P>Java: J2KDSE 1.4.1 </P>
<P>C/C++ 在两个编译器下完成，分别是 Digital Mars C/C++ v8.33,STL Port v4.5 和 Inter C/C++ v7.0 ，头文件和库用 VC++ 6.0 </P>
<P>用 Digital Mars 编译器的原因是：首先它能快速生成高质量代码，而且免费。其次是为了和 D 语言作比较，因为 D 语言用到相同的 C/C++ 连接器和很多 C 库。 </P>
<P>为了作彻底的性能比较，所有的编译器都用了速度最优的优化方案。 Inter 编译选项是 -QaxW ，它提供两种代码路径，一种是专门为 Pentium4+SIMD 优化，一种是为其他 cpu 优化。具体的代码运行路径在运行时自动选择。 ( 通过对不同 C/C++ 编译器性能的分析，我认为 Inter 编译器针对他自己芯片的优化非常好，而对于 Digital Mars 的编译器编译出来的代码性能，可以代表普遍的 C/C++ 性能 ) </P>
<P>对这几种语言作方方面面的比较是不太现实的，因为某些特定的功能并没有在这几种语言中都得到实现。比如 C 内建库里没有（字符串特定符号统计）函数。不过既然这篇文章的主题是讨论 C#, 所做的测试都是 C# 支持的，如果其他语言没有提供对应的功能，我们会提供一个自定义的实现。同时，如果某种语言对某种功能的内建支持非常差的话，我会考虑提供一个相对好一点的自定义支持。 ( 特别是对于 C/C++ ，至少有一两个地方看得出效率不高的内建库掩盖了语言本身的真实性能 ) </P>
<P><IMG height=348 src="http://mag.vchelp.net/200312/images/part1/image001.jpg" width=513></P>
<P>简而言之，我不会列出每种测试在不同语言的具体实现代码，不过在这里，我会详细介绍下面几种测试情况 : </P>
<P>noop.noop 测试的是程序的装载时间 : 一个空的 main/Main 的执行时间。跟其它测试不一样，这个测试是在 ptime 工具下完成的 .( 可以从 http://synesis.com.au/r_systools.html) 　获得，它提供了了 unix 环境下 time 工具类似的功能，同时它还能多次运行目标程序来获得平均值 ( 在我们的测试中， C/C++/D 运行 200 次 ,C# 运行 50 次， java 运行 30 次 ). 为了除去某些特定情况造成的影响，我们会多次测量 ( 译者注：指多次运行 ptime 程序， ptime 程序自身又会多次运行目标程序 ) ，并且取去掉最高值和最低值后的平均值。 </P>
<P>f2i 这个测试把 1 个长度为 10,000 的双精度浮点值转换为 32bit 的整数。从 Listing 1 可以看出，使用伪随机数产生算法可以保证各语言比较相同的浮点数。这带来了两个有趣的情况，我们将在查看结果的时候予以讨论。 </P>
<P><IMG height=501 src="http://mag.vchelp.net/200312/images/part1/image002.jpg" width=379></P>
<P>I2f 跟前一个相反，这个测试对针对具体某个函数进行的测试。程序生成 10000 个确定的伪随机整数，保证在不同语言中被转换的整数是相同的。 </P>
<P>I2str 把整型转换成成字符串型。整型变量从 0 循环增加到 5000000 ，每次做一个整型到字符串型的转换 ( 见 listing2).C#/Java 用内建函数完成 ---i.ToString() 和 Integer.Tostring(i),C/D 用传统的 sprintf() 完成。 C++ 分为两次完成。第一次用 iostream 的 strstream 类，和预期的一样，这种情况下的性能非常差。为了提高效率，我采取了一些措施，所以导致了第二种方案，见 i2str2 </P>
<P><IMG height=190 src="http://mag.vchelp.net/200312/images/part1/image003.jpg" width=251></P>
<P>Str2i. 把字符串转换成整型。建立一个字符串数组，内容是 i2str 转换过来的内容，然后再通过语言本身的支持或者库函数把字符串转换回去，计算转换回去的时间（见 listing3 ） .C/D 用 atoi 完成， c++ 用 iostream 的 strstream 完成， c# 用 int32.parse 完成， java 用 integer.parseint() 完成。 </P>
<P><IMG height=275 src="http://mag.vchelp.net/200312/images/part1/image004.jpg" width=256></P>
<P>picalc 迭代和浮点。通过 listing 4 所示的迭代算法计算圆周率。每种语言的实现其迭代次数是 10,000,000 。 </P>
<P><IMG height=328 src="http://mag.vchelp.net/200312/images/part1/image005.jpg" width=253><BR><BR></P>
<P>Picalcr. 递归。通过 listing5 所示的算法计算圆周率。对每种语言，我们会做 10000 次运算，考虑到 java 堆栈在深层地归时很容易被耗尽，所以每次运算限制在 4500 次递归以内。 </P>
<P><IMG height=378 src="http://mag.vchelp.net/200312/images/part1/image006.jpg" width=381><BR>Sieve. 叠代。通过 Listing 6 所示的迭代算法（ Eratosthenes's sieve （ http://www.math.utah.edu/~alfeld/Eratorthenes.html ））得到质数。每种语言的实现其迭代次数是 10,000,000 。 </P>
<P><IMG height=459 src="http://mag.vchelp.net/200312/images/part1/image007.jpg" width=391><BR></P>
<P>Strcat. 字符串连接 --- 通常是大量运行时开销的根源，也是导致程序低效的潜在领域。（我以前做过一个对性能敏感的项目，通过改善效率底下的字符串连接，我获得了 10% 的整体性能提升） . 测试通过构造四种不同类型的变量 :short,int, double 和一个 string ，然后把它们连接成一个 string 变量完成，连接时添加用分号分割．在连接的时候采用四种不同的连接方 </P>
<P>法 :1) 默认连接方法 2) 故意构造出来的导致效率低下的连接方法 3)C 语言种典型的 printf/Format 格式，需要重新分配空间 4) 为提高性能而进行的硬编码 . 代码摘录见 Listing7 </P>
<P><IMG height=272 src="http://mag.vchelp.net/200312/images/part1/image008.jpg" width=553><BR></P>
<P>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD width=12></TD></TR>
<TR>
<TD></TD>
<TD><IMG height=361 src="http://mag.vchelp.net/200312/images/part1/image009.jpg" width=554></TD></TR></TBODY></TABLE><BR>Strswtch. 通过级联 switch 进行字符串比较 . 通过语言支持的 switch 语句进行比较或者通过一些列的 if-else 语句模拟（ Listing 8 ）．这项测试是为了检验 c# 的字符串处理功能是否如他所声称的那样高效．测试分成两部分 : 首先是进行字符串之间的赋值，也就是同一身份的比较（译者注：也就是字符串内部的指针直接复制，不涉及到字符串内容的拷贝）．其次是通过字符串的拷贝后进行比较，这样就避免了同一身份比较而是进行进行字符串之间的字面比较．每种语言上都进行 850000 次叠代，因为超过这个次数后会导致 java vm 内存耗尽． 
<P></P>
<P>strtok 字符串分段。字符串的运行效率也极低，有时甚至引发严重的运行开销。这个测试（ Listing 9）读取一个文本文件的全部内容至一个字符串，然后拆分成段。第一个变量用字符分隔符：‘；'分隔字符串，并忽略所有的空白。第二个变量也用分号分隔，但保留了空白。第三个和第四个变量用字符串分隔符：<->，并相应的忽略和保留了空白处。C里仅第二个变量用了strtok()，C++四个变量都用STLSoft的string_tokeniser模板，但各自正确给参数赋值。C#第一个变量用System.String.Split()，2至4变量用SynSoft.Text.Tokeniser.Tokenise()；Java用java.util.StringTokenizer，仅支持变量2和4（注：我选用STLSoft的string_tokeniser模板不是我要为STLSoft作宣传，而是测试表明它比Boost的快2.5倍，而Boost是大多数开发人员在查找库时的首选）。 </P>
<P><IMG height=255 src="http://mag.vchelp.net/200312/images/part1/image010.jpg" width=553><BR><STRONG>结果 </STRONG></P>
<P>在 noop测试中，表2列出了以微秒（us）为单位装载一个空操作的平均开销。显然C，C++，D的差别不合逻辑，每一个大约是5-6毫秒，而Intel确低至2ms。我所想到的唯一可能是这个编译器优化了所有的C实时运行库的初始化工作。如果是这样，这个性能在其他非试验性即实际应用中不能体现出来，因为它至少需要一些C … ，这有待深入研究。 </P>
<P><IMG height=111 src="http://mag.vchelp.net/200312/images/part1/image011.jpg" width=519><BR>
<P></P>
<P>C#用了大约70ms，Java约0.2秒。C#和Java的装载清晰的表明其实时开销在于它们的实时运行的结构基础（VM和支持DLLs）。但是除了大规模的命令行的商业化批处理，对极大型系统（上百万条代码）或CGI的基础构建，启动开销在大多数情况下不重要。重荷服务器允许在数秒内启动，所以语言间200ms的差异就微乎其微了。 </P>
<P><IMG height=305 src="http://mag.vchelp.net/200312/images/part1/image012.jpg" width=554><BR>其余的结果分三部分显示。图 2和3分别展示了字符串连接和分段的结果。图1包含了其余全部的测试结果，我们现在先来看看它。 
<P></P>
<P><IMG height=284 src="http://mag.vchelp.net/200312/images/part1/image013.jpg" width=554><BR></P>
<P><IMG height=290 src="http://mag.vchelp.net/200312/images/part1/image014.jpg" width=554><BR></P>
<P>图 1中，对各测试项，C，C++，D，Java的结果均以其相应测试项C＃运行时间的百分比显示。因此，在100%线上下波动的结果说明与C＃性能相当。高百分比说明C#性能较优，而低百分比表明C#性能相对较差。 <BR><BR></P>
<P><STRONG>f2i </STRONG>抛开Intel不谈，可以看出C#在浮点到整型的转换中效率远远高于其余四个，仅是C，C++， 和D开销的2/3,是Java的2/5。在我让它们使用相同的随机数发生器前，不同语言的性能差异极大，这说明转换速度非常依赖具体的浮点数值，当然这是后见之明了。为了确保每个语言做相同的转换，程序计算转换值的总和，这也有助于任何过度的优化去除整个循环。可以得知这种转化在语言间正确度不同。每种语言的计算总和迭代10,000次的结果在表3中列处。它们表明（虽然无法证实）C，C++，和D是最正确的，Java其次，C#最差。C#牺牲正确性和降低处理器浮点精度（参见 引用）换取优越性能是令人信服的，但事实上做此判断前我还需要更多的证据。至于Intel，说它的性能没有什么更卓越的也是公平的。 </P>
<P><IMG height=66 src="http://mag.vchelp.net/200312/images/part1/image015.jpg" width=554><BR></P>
<P>5个语言这里效率相当。另4种性能比C#低5%以内。唯一叫人感兴趣的是这四个语言都是性能比C#略好。这表明C#确实效率低，虽然在这方面只低一点。这与从整型到浮点型转换的结果相反。同样，值得我们注意的是，虽然Java在浮点到整型的转换中性能很低，但在整型到浮点的转换中却是最好的，比C#高10%。对f2i，Intel表现极佳，其C和C++分别只用了C#时间的10%和23%。 </P>
<P><STRONG>i2str </STRONG>整型到字符型的转换。 C#比C和D的printf略好。比C++的以iostream为基础的转换效率要高。它是C#运行时间的4倍（对Intel 和VC6 STL/CRT是5倍）。但Java性能出色，用了C#2/5的时间。因为C++的效率低下，第二个变量使用STLSoft的 <STRONG>integer_to_string<> </STRONG>模板函数。它在很多编译器上比C和C++库的所有可利用的转换机制要快。在这种情况下，性能比C#高10%，比C和D高约20%。但是因为Intel编译器的优化影响， <STRONG>integer_to_string<> </STRONG>似乎找到了其完美搭档：其时间比C#低30%，是第三个速度超过Java的，比Iostream快17倍以上。认为它很可能已经接近了转换效率的上限也是合理的。 </P>
<P><STRONG>str2i </STRONG>字符型到整型的转化结果与前面大相径庭。 C和D用atoi比C#快约5-8倍，比Java快3倍，Java比C#效率高得多。但是这四个比C++以iostream为基础的转换都快。C++用 Digital Mars/STLPor运行时间t 是 C#的2.5倍，用 Intel和VC6 CRT/STL 是 10倍。这是iostream的另一个致命弱点。但因此说C++效率低下未免有失公允（实际上，我正在为 STLSoft 库写字符至整型的转换，以和它 <STRONG>integer_to_string<> </STRONG>的杰出转换性能相匹配。最初的结果表明它将优于 C的atoi，所以这个转换中C++也是优于C的。我希望能在本文刊发前完成此转换。请参阅 <A href="http://stlsoft.org/conversion_library.html">http://stlsoft.org/conversion_library.html </A>。 ） </P>
<P><STRONG>picalc </STRONG>在这里各语言性能表现相近。 C#和Java几乎相同，比C，C++效率高上10%。C#的性能可以这样解释：它的浮点数操作效率高，这可以从f2I看出。但这不能理解Java的表现。我们可以认为C#和Java都能较好的优化循环，它涉及函数调用，但这有赖于深入研究。既然性能相差10%以内，我们只能得出这样的结论：各语言在循环结构性能方面差异不显著。有趣的是，Intel的优化这里没有实际效果，可能是因为Pi值计算太简单了吧。 </P>
<P><STRONG>Picalcr </STRONG>: 这里的结果可以更加确定一点， C ， C++ ， C# 和 D 语言的性能偏差在 2% 之内，我们可以公平地说它们对于递归执行的性能是一样的。 Java 却是花费比其他四种语言多 20% 的时间，加上 JVM （ Java 虚拟机）在超过 4500 次递归时堆栈耗竭，可以明显地从速度和内存消耗方面得出 Java 处理递归不是很好。 Intel 的优化能力在这里有明显的作用（让人印象深刻的 10% ），但我在这里并没有详细分析这一点。 </P>
<P><STRONG>Sieve: </STRONG>相对简单的计算质数的算法（仅包含迭代和数组访问）说明 C ， C++ ， C# 和 D 语言在这方面事实上是一样的（仅 0.5% 的偏差），但 Java 的性能低了 6% 。我认为这中间大有文章， C ， C++ 代码不进行数组边界安全检查，而 C# 和 Java 进行数组边界检查。我对这种结果的解释是： C# 和 D 语言在 FOR 循环内能根据对数组边界的条件测试对边界检查进行优化，而 Java 不行。由于 C# 是新兴语言， D 语言还没发行，这就让人不那么失望了。即使这不是为 Java 辩解，也不会让人印象深刻了。再说， Intel 优化起了明显的作用——增长 5% 性能，正如 picalcr 例子，这相对于其他语言更让人印象深刻。 </P>
<P><STRONG>Strswtch </STRONG>: 这个测试对于 C# 语言不是很好。即使是 C++ 低效的字符串类操作符 = = 都比 C# 的性能快 2.5 倍 (Digital Mars) 到 5 倍（ Intel VC6 STL/CRT ）。 .NET 环境下字符串是内部处理的，这表示字符串存储在全局离散表中，副本可以从内存中消除，等值测试是以一致性检查的方式进行的 ( 只要两个参数都是在内部 ) 。这明显表示内部修复机制的效率严重低下或者一致性检查没有建立 C# 的 ”string-swatching” 机制性。 C# 的这种严重低效率更让人倾向于后者，因为想不到个好的理由解释为什么会这样。正如例 8 所示，变量 1 的字符串实例是文字的，都在内部（事实上，这里有个限制没有在例 8 中表示出来，在变量 1 的预循环中用来验证参数是真正在内部的）。 </P>
<P>当在变量 2 中假装进行一致性检查，发现仅 Java 的性能有明显的提高，从 2% 到令人印象深刻的 30% 。很明显 Java 在使用一致性检查所取的性能是不切实际的，因为在真正的程序中不能限制字符串都是文字的。但这可用来说明支持内部处理和提供可编程实现访问这种机制的语言的一种性能。讽刺的是， C# 作为这五种语言之一，它的性能是最差的。 </P>
<P><STRONG>Strcat </STRONG>: 表 2 以微秒（ μs ）显示了五种语言每一种在这个例子中的四个变量的花费时间。本文没有足够的篇幅来细节描述 17 执行代码，但尽量保持例子 7 的四个变量的真实面貌。变量 1 和 2 在所有语言执行代码中涉及以堆栈为基础的字符串内存，同时 C ， D 语言在第三个变量中、 C ， C++ ， D 语言在第四个变量中用结构内存，这在某种程度说明他们的性能优于其他变量和语言的执行代码。本例子的 C ， C++ 的讨论适合于 Digital Mars 版本，因为很明显的 Intel 所带来的性能提高被 Visual C++ 运行时刻库的效率（非期望）所抵消，事实上 Intel 的每种测试的性能都比 Digital Mars 的差。 </P>
<P>第一种测试是以默认的方式执行的，很显然 C# 和 D 语言的性能都好于其它语言。很奇怪的是 Java 的性能最差，因为 Java 被认为有能力在一条语句中把字符串序列解释成字符串构件格式（在变量 4 中手工转换）。 </P>
<P>第二种测试，众所周知它的格式不好，透露了很多东西。它的劣性根据语言而不同，对于 C# ， D 和 Java ，包含在连续的表达式中连接每一项，而不是象第一个变量在一个表达式中。对于 C ，它简单地省略了在开始多项连接重新分配之前的的内存请求。对于 C++ ，它使用 strstream 和插入操作符而不是使用 str::string 和 + （）操作符。结果显示所有语言不同程度地降低性能。 C 内存分配器的附加轮询看起来没有起多大作用，可能因为没有竞争线程造成内存碎片，所以消耗同样的内存块。 C++ 改变成 iostreams 使代码更紧凑，但没有转变数字部分，它们仅被插入到流当中，所预计的执行结果验证了这一点。 D 因为这个改变降低性能许多（ 32% ）， Java 更多（ 60% ）。很奇怪的是 C# 性能在这里仅降低很小，少于 7% 。这让人印象非常深刻，意味着可能在 .NET 的开发环境下因为 Java 字符串连接而形成的代码回查的警觉本能会衰退。 </P>
<P>第三种测试使用 printf()/Format() 的变量不足为奇。通过使用部分 / 全部的帧内存， C ， C++ 和 D 达到很好的性能。奇怪的是 C# 的性能仅是 22% ，简直不值一提。 Java 根本没有这个便利功能。（以个人的观点也是不值一提的） C#和D语言都能够在封闭的循环中，依照数组边界测试情况，优化他们的越界检查。 </P>
<P>最大的 测试 －硬编码的性能－这个是很有趣的问题，因为 C++还是能提供超级的性能，如果能够依照问题的精确领域来编码，即使别的语言（C#,java）使用默认的或者性能优化的组件或者理念也是如此。在C#和java上使用他们各自的StringBuilders能够提供真实的效果，达到他们在这个集合中的最佳性能。然而，C#能发挥最佳效率的机制，还是不能与C/C++、D语言相比。更糟糕的是，java的最佳性能都比别的语言的最差性能要可怜，这是相当可悲的。 </P>
<P>总之，我们说对底层直接操作的语言垂手可得地赢得那些转化率工作得很好的或者完全地在中间代码中，并且很显然地能够提供良好性能的语言用法对java是很重要的而对C#是不太重要的。请注意这一点。 </P>
<P><STRONG>Strtok </STRONG>. Table3显示了五种语言的每一个环节总共的时间（单位是毫秒ms）。同时他们的空间占用在这篇文章中并没有提及，但是所有语言的实现都是非常直接的并且很明显很接近在Listing9中列出的四个问题的本质。与strcat一样，基于同样的原因，使用Intel编译器的结果没有讨论。 </P>
<P>第一个问题――分割字符，保留空格――显示C++版本是明显的胜利者，D语言要慢20％，C# 要3倍的时间。显然，这个与库的特征的关系比语言的更明显， </P>
<P>并且我使用 tokenizer比著名的Boost更多一些，那样的话，D语言要比C++快2倍，并且C＃只慢20％。虽然如此，STLSoft的tokenizer是免费得到,我想我们应该把这个作为C++的一个胜利。（其实，template在这一点上的使用STL的basic_string<char>作为他的值类型，这个并不因为他的良好性能而闻名，这可能引起争论－我并没有使用一个更有效率的string，就像STLSoft的frame_string.总之，我认为这是个公平比较）。 </P>
<P>第二个问题－－分割字符，去掉空格－－包含所有语言的实现版本。自然地，C语言赢得了比赛，它使用了strtok()函数，这个函数在创建tokens时并不分配内存并且直接写终结符NULL到tokenized string的结尾。尽管有这些不利条件，C++的性能还是很好的，时间是C的221％，比较好的是D语言，432％。 </P>
<P>C＃和java就很差了，分别是746％和957％。这简直是不敢相信的，C＃和java运行的时间是C++的3.4倍和4.3倍，这三种语言都为tokens分配了内存。我相信这个是STL模型在处理iterable sequences时比passing around arrays更有效率的很好的例子。（请注意，我们有高度的信心这是场公平的关于C++,C#和D语言的比较，我写了三种tokerizer的实现，都是公开的，可得到的.) </P>
<P>第三个问题－－分割句子，保留空格--显示了两件事情。第一，在这三种语言在实现这个程序的代价比实现单个字符更昂贵。第二，我们发现D语言取代了C++的最佳性能的地位，领先5％左右。C＃继续被甩下，大概是另外两种语言的1.8倍时间左右。 </P>
<P>第四个问题－－分割句子，去掉空格--没有任何令人意外的，C++和D拥有大概一致的性能。C＃比较不错（大概2倍的性能差距）java更慢（差不多3倍时间）。对于C++，C＃和D来说去掉空格比不去掉空格导致一小点的性能损失。很明显的，在C＃中，这么做比其他两个的损失更大一点。很有价值的是，这些功能在C＃和D中是已经实现的。由于数组在C#中是不可变的，从tokens中返回一个没有空格的数组将导致重新分配一个数组。可是，D允许指派数组的长度，这个能动态地调整数组的大小这是个很好的例子说明D语言提供更好的效率。 </P>
<P>总的来说，考虑到性能和自由度，我们能够说 C++是胜利者，D是很接近的第二名。C拥有最佳的性能，但是只支持一种类型的tokerization。C＃表现得很差，java更甚。 </P>
<P><STRONG>总结 </STRONG></P>
<P>我们可以把结果分成3种情况：这些包括语言的特性，包括库的特性，或者两者都相关。这些只与语言相关的特性显示在 f2i, i2f, picalc, picalcr, and sieve scenarios,这些在语言选择上的作用是很小的。c＃看起来在总体上是最好的，但是有点让人不能信服的是它在f2i中取得的优异性能并且是因为牺牲了浮点精确度而取得效率。（这一点需要更多大检查，我将在下一篇文章中去做。） </P>
<P><STRONG>java是明显最差的 </STRONG>。 </P>
<P>当讨论intel编译器的浮点计算的深度优化效果时，它很明显地显示，c和c++能够做的比C＃更好。我讨论这些并不是说这些语言必须要更好的效率，而是说他们能比其他的语言提供更好的优化和增强的机会。这个大概是与本主题完全不相干的是，C和C++比别的语言有更多的编译器；更多的竞争，更聪明的人拥有不同的策略和技术。编译器的结果的运行目标是处理器，这将导致戏剧性的效果；我们仅仅在表面上探讨了这个庞大的问题，但是我不久将在以后的文章中回到这个主题。 </P>
<P>Strtok 是唯一一个能说是library-only的，在这一点上C＃干的并不好。虽然比java快，但是比其他的语言慢2倍或更多。同样令人失望的是VisualC/C++的运行库。在这一点上包括语言和库的效果的是i2str,str2i,strcat和strtswtch，描绘的并不是很清楚。C＃明显的在string的拼接这一环节上比java好得多，但是明显地比其他的差。关于尊敬的C，C++,D，只是在一些环节上领先，在另外一些环节上相当地差。 </P>
<P>很有趣的是定制的C++的库替换加上深度优化的intel的编译器。 </P>
<P>总之，不可能做出定量的结论。从语言的表层来看， C# 和 Java 挺简单的，但低效的库降低了速度。我觉得，当相关的库还不如语言本身的时候， C# 的确实有微弱的优势；但反过来，当库逐渐完善，超过语言本身后， C# 明显就不行了。但还是能看到一点点希望： C# 还是有可能实现预期的目标，因为库比语言更容易实现组装。但是，人们认为由于跟别的语言相比， C# 和 Java 所用的库与语言结合的更加紧密，这些库就可以作为这两种语言效率的关键所在，至少对当前的版本来说是这样的。 </P>
<P>正如我所作出的示范（从整数到字符）以及相关提到的（从字符到整数），有人可能会批评者并不符合这篇文章的原意。 </P>
<P><STRONG>更深入的探讨 </STRONG></P>
<P>本文主要探讨了一下 C# 对 C++ 和 Java 可能造成的“威胁”。总的来说，这结果虽然不怎么鼓舞人心，却足以让人吃惊。从效率上看， C# 跟 C 以及 C++ 这些过去的同类产品（假如它们是更高效的）相比只能算是一般水平，至少在基本语言特征的比较上是这样的。从某种程度上来说， Java 也是如此。（我承认我无法证明 C 和 C++ 相对于 C# 的绝对优势。在多年前我的毕业论文分析调查期间，我认识到令人乏味的结果正和令人兴奋的结果一样生动形象，但前者花费更小的经济支出。） </P>
<P>上述结论对于多处理器以及（高性能的文件／网络处理系统）等来说却不一定适用。不过，这确实很好地展现出了这些语言的基本效率以及更复杂的运算所依赖的最基本的库。 </P>
<P>就 D 发展状况来看，现在就对它进行这样的归类似乎为时过早，不过它现在的表现确实不错。 D 的效率至少是 C# 的 167% ，其实大多数情况下还不止。有些人指出，目前仅仅还处于字符时代，只需几个人就能完成一个编译器及其附带的库。我猜测， D 有可能发展成一门强大的技术。 </P>
<P>就我个人而言，作为一个对 C# 持有偏见的人，偶然还是会对它的性能感到惊讶。我热切地期盼着能将性能的对比分析延伸到更多领域：复杂而高效的内存的应用，多线程，进程通信，特殊文件处理，手写代码开销，这些技术都能让我们对语言进行更深入的研究。能看到 Inter 公司怎样通过定制的 C Library （比如 CRunTiny ；参考 http://cruntiny.org/ ）和 STL （比如 STLPort ）。 </P>
<P><STRONG>鸣谢 </STRONG></P>
<P>感谢 Walter Bright 为我提供了一份言简意赅的关于 D 的介绍，并及时地指出了一些当时被我忽略的优点，使我能对测试工程进行改进。同时也要感谢 Sun Microsystems 公司的 Gary Pennington ，他为我提供了关于 Java 方面的资料。还有 Scott Patterson ，他对本文的草稿进行了大量的精简，并对全篇文章进行了细致的检查。 <BR><BR></P><![CDATA[<P>C# 性能能赶上编译型的 C/C++/D 和中间代码运行时解释的 java 吗？ </P>
<P>微软发布了 .net 平台和 .net 平台支持的多种语言，包括 C#,vb.net, 受管 (managed extensions) 的 C++. 它们构成的体系能同时满足开发出高效的服务器架构，和开发部门对强壮的多功能的接口的需求． </P>
<P>和其他众多程序员一样，拿新开发语言与日常用到的语言作比较和选择的时候，我对新玩意总有莫名的偏见和怀疑．本篇文章将会讨论 .net 平台的优点，特别是相关的 C#. 我们拿 C# 和其他开发语言在高性能软件开发方面，作定量的性能比较． </P>
<P>因为 c# 程序编译运行分为两部分：先编译成中间代码，然后是中间代码的运行时解释执行，所以我们不仅会把他跟纯编译型的 C/C++/D 语言相比，也会把他与典型的 ( 中间代码运行时解释 ) 的 Java 语言作比较． Java 无疑是一个成熟的语言，应用在各个计算机领域． Java 程序先被编译成中间代码，然后运行时通过 java 虚拟机解释成本地机器码执行．由此看来， .net 平台，特别是为首的 c# 语言，借鉴了 java 的很多东西，包括语法和运行时支持． </P>
<P>对新语言（或者说时语言 + 对应的库 + 该语言的运行时支持）作完整公平的性能比较算是一个庞大考验．本文对 c# 与 C/C++/D 就开发中都涉及到的基本共通点作初步的性能比较，同时还包括各个语言所带的库的性能比较．包括： </P>
<P>1. 运行一次完整的程序（转载执行一个程序的时间） </P>
<P>2. 算法和递归（计算圆周率和 Eratorshnes's sieve ） </P>
<P>3. 类型转换（ int->float,float->int,int->string.string->int ） </P>
<P>4. 字符串比较 </P>
<P>5. 字符串连接 </P>
<P>6.( 字符串特定符号统计 ) </P>
<P>通过上面的一系列比较，将会揭示 c# 编译器效力的一些有意思的地方和 .net 运行库的性能 </P>
<P><STRONG>背景 </STRONG><STRONG>: </STRONG></P>
<P>虽然作比较的语言都可以算是 C 语言系列，虽然这五种语言在语法上只有或多或少的不同，但在其他方面，它们的差别是巨大的．（见表一， http://www.digitalmars.com/d/comparison.html ） . 我假设读者已经熟悉 C/C++/Java ，以及一些基本的 C# ．对于 D 语言，不如其他语言那么有名，但你可以找到它的介绍 </P>
<P><STRONG>D </STRONG><STRONG>语言介绍 </STRONG></P>
<P>D 语言是 Walter Bright 正在开发的一种语言，安装和学习这种语言的开销很小。实质上， D 语言的目标是更成功的 C/C++ 语言，改进包括可变长度的数组，函数参数的 [in][out] 属性，自动内联，版本控制，局部函数，内建单元测试，严格的类型检查等等，包括 Java/C# 中实现的忽略指针和引用的差别 , 这在 C++ 中只有通过模板实现。它同时支持对现有 C 函数和 API 的直接支持。 </P>
<P>它支持许多跟 Java/C# 类似的特性 , 比如垃圾收集。 D 语言跟 C#/Java 的本质区别是 D 语言运行时不需要运行时的虚拟机支持。 D 语言所支持的所有特性都是在编译连接时完成的，所以不需要运行时的额外支持。 D 语言的开发目前已经到了 Alpha 版本，将来的成熟版本在绝大多数情况下将会比 Java/C# 高效得多，除非 JIT 优化非常明显．所以我们才把 D 语言加到我们的测试当中。实际上， Walter 告诉我们， D 完全有能力产生跟 C 代码运行得一样快的代码，同时，由于自动内联，内建数组，字符串内建优化，非 \0 的字符串结束标记等特性，他相信在 D 语言正式发布的时候，它的性能将会超过 C 。 </P>
<P>在真正的测试开始以前，我假定性能按照从高到低排序，应该是 C/C++,D,C#,Java ．我跟其随后证实的其他好多人一样，一开始就怀疑 C# 产生高性能代码的能力． </P>
<P><STRONG>性能比较 </STRONG></P>
<P>我们会在下面接着介绍的十一种不同情况下测试性能．测试平台是 2G 512M 的 pentium4 ，操作系统是 Windows XP Pro. 测试是在一套我自己设计的工具下进行的，为了符合这篇文章，我理所当然地用 C# 写了这个工具。在没有其他干扰进程的情况下，对每个测试者运行七次，分别去掉最快的一次和最慢的一次后得到的品均值，就是测试者的成绩。除了下面说到的 noop 测试，所有的测试通过高精确度定时器计算完成所有内循环所需时间完成。（译者注：比如 int->float 测量中，会通过循环运行多个 int->float 的转换，然后测量完成整个循环需要的时间）。如 Listing1 所示的代码，就是用 c# 完成的 f2i 测试。 (C 通过调用 win32 api 函数 QueryPerformanceCounter() ； C++ 通过 WinSTL 的 performance_counter;C# 通过 SynSoft.Performance.PerformanceCounter;D 通过 syncsoft.win32.perf.PerfomanceCounte ； Java 通过 System.currentTimeMillis()) 。每个程序都包含进入实质测试代码前一段 ” 热身代码 ” 来减少程序初始化 ,catch 等因素带来的不利影响。 </P>
<P>编译器版本号如下： </P>
<P>C#:VC#.net v7.00.9466,.NET Framework v1.0.3705; </P>
<P>D: Digital Mars D compiler Alpha v0.61 </P>
<P>Java: J2KDSE 1.4.1 </P>
<P>C/C++ 在两个编译器下完成，分别是 Digital Mars C/C++ v8.33,STL Port v4.5 和 Inter C/C++ v7.0 ，头文件和库用 VC++ 6.0 </P>
<P>用 Digital Mars 编译器的原因是：首先它能快速生成高质量代码，而且免费。其次是为了和 D 语言作比较，因为 D 语言用到相同的 C/C++ 连接器和很多 C 库。 </P>
<P>为了作彻底的性能比较，所有的编译器都用了速度最优的优化方案。 Inter 编译选项是 -QaxW ，它提供两种代码路径，一种是专门为 Pentium4+SIMD 优化，一种是为其他 cpu 优化。具体的代码运行路径在运行时自动选择。 ( 通过对不同 C/C++ 编译器性能的分析，我认为 Inter 编译器针对他自己芯片的优化非常好，而对于 Digital Mars 的编译器编译出来的代码性能，可以代表普遍的 C/C++ 性能 ) </P>
<P>对这几种语言作方方面面的比较是不太现实的，因为某些特定的功能并没有在这几种语言中都得到实现。比如 C 内建库里没有（字符串特定符号统计）函数。不过既然这篇文章的主题是讨论 C#, 所做的测试都是 C# 支持的，如果其他语言没有提供对应的功能，我们会提供一个自定义的实现。同时，如果某种语言对某种功能的内建支持非常差的话，我会考虑提供一个相对好一点的自定义支持。 ( 特别是对于 C/C++ ，至少有一两个地方看得出效率不高的内建库掩盖了语言本身的真实性能 ) </P>
<P><IMG height=348 src="http://mag.vchelp.net/200312/images/part1/image001.jpg" width=513></P>
<P>简而言之，我不会列出每种测试在不同语言的具体实现代码，不过在这里，我会详细介绍下面几种测试情况 : </P>
<P>noop.noop 测试的是程序的装载时间 : 一个空的 main/Main 的执行时间。跟其它测试不一样，这个测试是在 ptime 工具下完成的 .( 可以从 http://synesis.com.au/r_systools.html) 　获得，它提供了了 unix 环境下 time 工具类似的功能，同时它还能多次运行目标程序来获得平均值 ( 在我们的测试中， C/C++/D 运行 200 次 ,C# 运行 50 次， java 运行 30 次 ). 为了除去某些特定情况造成的影响，我们会多次测量 ( 译者注：指多次运行 ptime 程序， ptime 程序自身又会多次运行目标程序 ) ，并且取去掉最高值和最低值后的平均值。 </P>
<P>f2i 这个测试把 1 个长度为 10,000 的双精度浮点值转换为 32bit 的整数。从 Listing 1 可以看出，使用伪随机数产生算法可以保证各语言比较相同的浮点数。这带来了两个有趣的情况，我们将在查看结果的时候予以讨论。 </P>
<P><IMG height=501 src="http://mag.vchelp.net/200312/images/part1/image002.jpg" width=379></P>
<P>I2f 跟前一个相反，这个测试对针对具体某个函数进行的测试。程序生成 10000 个确定的伪随机整数，保证在不同语言中被转换的整数是相同的。 </P>
<P>I2str 把整型转换成成字符串型。整型变量从 0 循环增加到 5000000 ，每次做一个整型到字符串型的转换 ( 见 listing2).C#/Java 用内建函数完成 ---i.ToString() 和 Integer.Tostring(i),C/D 用传统的 sprintf() 完成。 C++ 分为两次完成。第一次用 iostream 的 strstream 类，和预期的一样，这种情况下的性能非常差。为了提高效率，我采取了一些措施，所以导致了第二种方案，见 i2str2 </P>
<P><IMG height=190 src="http://mag.vchelp.net/200312/images/part1/image003.jpg" width=251></P>
<P>Str2i. 把字符串转换成整型。建立一个字符串数组，内容是 i2str 转换过来的内容，然后再通过语言本身的支持或者库函数把字符串转换回去，计算转换回去的时间（见 listing3 ） .C/D 用 atoi 完成， c++ 用 iostream 的 strstream 完成， c# 用 int32.parse 完成， java 用 integer.parseint() 完成。 </P>
<P><IMG height=275 src="http://mag.vchelp.net/200312/images/part1/image004.jpg" width=256></P>
<P>picalc 迭代和浮点。通过 listing 4 所示的迭代算法计算圆周率。每种语言的实现其迭代次数是 10,000,000 。 </P>
<P><IMG height=328 src="http://mag.vchelp.net/200312/images/part1/image005.jpg" width=253><BR><BR></P>
<P>Picalcr. 递归。通过 listing5 所示的算法计算圆周率。对每种语言，我们会做 10000 次运算，考虑到 java 堆栈在深层地归时很容易被耗尽，所以每次运算限制在 4500 次递归以内。 </P>
<P><IMG height=378 src="http://mag.vchelp.net/200312/images/part1/image006.jpg" width=381><BR>Sieve. 叠代。通过 Listing 6 所示的迭代算法（ Eratosthenes's sieve （ http://www.math.utah.edu/~alfeld/Eratorthenes.html ））得到质数。每种语言的实现其迭代次数是 10,000,000 。 </P>
<P><IMG height=459 src="http://mag.vchelp.net/200312/images/part1/image007.jpg" width=391><BR></P>
<P>Strcat. 字符串连接 --- 通常是大量运行时开销的根源，也是导致程序低效的潜在领域。（我以前做过一个对性能敏感的项目，通过改善效率底下的字符串连接，我获得了 10% 的整体性能提升） . 测试通过构造四种不同类型的变量 :short,int, double 和一个 string ，然后把它们连接成一个 string 变量完成，连接时添加用分号分割．在连接的时候采用四种不同的连接方 </P>
<P>法 :1) 默认连接方法 2) 故意构造出来的导致效率低下的连接方法 3)C 语言种典型的 printf/Format 格式，需要重新分配空间 4) 为提高性能而进行的硬编码 . 代码摘录见 Listing7 </P>
<P><IMG height=272 src="http://mag.vchelp.net/200312/images/part1/image008.jpg" width=553><BR></P>
<P>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD width=12></TD></TR>
<TR>
<TD></TD>
<TD><IMG height=361 src="http://mag.vchelp.net/200312/images/part1/image009.jpg" width=554></TD></TR></TBODY></TABLE><BR>Strswtch. 通过级联 switch 进行字符串比较 . 通过语言支持的 switch 语句进行比较或者通过一些列的 if-else 语句模拟（ Listing 8 ）．这项测试是为了检验 c# 的字符串处理功能是否如他所声称的那样高效．测试分成两部分 : 首先是进行字符串之间的赋值，也就是同一身份的比较（译者注：也就是字符串内部的指针直接复制，不涉及到字符串内容的拷贝）．其次是通过字符串的拷贝后进行比较，这样就避免了同一身份比较而是进行进行字符串之间的字面比较．每种语言上都进行 850000 次叠代，因为超过这个次数后会导致 java vm 内存耗尽． 
<P></P>
<P>strtok 字符串分段。字符串的运行效率也极低，有时甚至引发严重的运行开销。这个测试（ Listing 9）读取一个文本文件的全部内容至一个字符串，然后拆分成段。第一个变量用字符分隔符：‘；'分隔字符串，并忽略所有的空白。第二个变量也用分号分隔，但保留了空白。第三个和第四个变量用字符串分隔符：<->，并相应的忽略和保留了空白处。C里仅第二个变量用了strtok()，C++四个变量都用STLSoft的string_tokeniser模板，但各自正确给参数赋值。C#第一个变量用System.String.Split()，2至4变量用SynSoft.Text.Tokeniser.Tokenise()；Java用java.util.StringTokenizer，仅支持变量2和4（注：我选用STLSoft的string_tokeniser模板不是我要为STLSoft作宣传，而是测试表明它比Boost的快2.5倍，而Boost是大多数开发人员在查找库时的首选）。 </P>
<P><IMG height=255 src="http://mag.vchelp.net/200312/images/part1/image010.jpg" width=553><BR><STRONG>结果 </STRONG></P>
<P>在 noop测试中，表2列出了以微秒（us）为单位装载一个空操作的平均开销。显然C，C++，D的差别不合逻辑，每一个大约是5-6毫秒，而Intel确低至2ms。我所想到的唯一可能是这个编译器优化了所有的C实时运行库的初始化工作。如果是这样，这个性能在其他非试验性即实际应用中不能体现出来，因为它至少需要一些C … ，这有待深入研究。 </P>
<P><IMG height=111 src="http://mag.vchelp.net/200312/images/part1/image011.jpg" width=519><BR>
<P></P>
<P>C#用了大约70ms，Java约0.2秒。C#和Java的装载清晰的表明其实时开销在于它们的实时运行的结构基础（VM和支持DLLs）。但是除了大规模的命令行的商业化批处理，对极大型系统（上百万条代码）或CGI的基础构建，启动开销在大多数情况下不重要。重荷服务器允许在数秒内启动，所以语言间200ms的差异就微乎其微了。 </P>
<P><IMG height=305 src="http://mag.vchelp.net/200312/images/part1/image012.jpg" width=554><BR>其余的结果分三部分显示。图 2和3分别展示了字符串连接和分段的结果。图1包含了其余全部的测试结果，我们现在先来看看它。 
<P></P>
<P><IMG height=284 src="http://mag.vchelp.net/200312/images/part1/image013.jpg" width=554><BR></P>
<P><IMG height=290 src="http://mag.vchelp.net/200312/images/part1/image014.jpg" width=554><BR></P>
<P>图 1中，对各测试项，C，C++，D，Java的结果均以其相应测试项C＃运行时间的百分比显示。因此，在100%线上下波动的结果说明与C＃性能相当。高百分比说明C#性能较优，而低百分比表明C#性能相对较差。 <BR><BR></P>
<P><STRONG>f2i </STRONG>抛开Intel不谈，可以看出C#在浮点到整型的转换中效率远远高于其余四个，仅是C，C++， 和D开销的2/3,是Java的2/5。在我让它们使用相同的随机数发生器前，不同语言的性能差异极大，这说明转换速度非常依赖具体的浮点数值，当然这是后见之明了。为了确保每个语言做相同的转换，程序计算转换值的总和，这也有助于任何过度的优化去除整个循环。可以得知这种转化在语言间正确度不同。每种语言的计算总和迭代10,000次的结果在表3中列处。它们表明（虽然无法证实）C，C++，和D是最正确的，Java其次，C#最差。C#牺牲正确性和降低处理器浮点精度（参见 引用）换取优越性能是令人信服的，但事实上做此判断前我还需要更多的证据。至于Intel，说它的性能没有什么更卓越的也是公平的。 </P>
<P><IMG height=66 src="http://mag.vchelp.net/200312/images/part1/image015.jpg" width=554><BR></P>
<P>5个语言这里效率相当。另4种性能比C#低5%以内。唯一叫人感兴趣的是这四个语言都是性能比C#略好。这表明C#确实效率低，虽然在这方面只低一点。这与从整型到浮点型转换的结果相反。同样，值得我们注意的是，虽然Java在浮点到整型的转换中性能很低，但在整型到浮点的转换中却是最好的，比C#高10%。对f2i，Intel表现极佳，其C和C++分别只用了C#时间的10%和23%。 </P>
<P><STRONG>i2str </STRONG>整型到字符型的转换。 C#比C和D的printf略好。比C++的以iostream为基础的转换效率要高。它是C#运行时间的4倍（对Intel 和VC6 STL/CRT是5倍）。但Java性能出色，用了C#2/5的时间。因为C++的效率低下，第二个变量使用STLSoft的 <STRONG>integer_to_string<> </STRONG>模板函数。它在很多编译器上比C和C++库的所有可利用的转换机制要快。在这种情况下，性能比C#高10%，比C和D高约20%。但是因为Intel编译器的优化影响， <STRONG>integer_to_string<> </STRONG>似乎找到了其完美搭档：其时间比C#低30%，是第三个速度超过Java的，比Iostream快17倍以上。认为它很可能已经接近了转换效率的上限也是合理的。 </P>
<P><STRONG>str2i </STRONG>字符型到整型的转化结果与前面大相径庭。 C和D用atoi比C#快约5-8倍，比Java快3倍，Java比C#效率高得多。但是这四个比C++以iostream为基础的转换都快。C++用 Digital Mars/STLPor运行时间t 是 C#的2.5倍，用 Intel和VC6 CRT/STL 是 10倍。这是iostream的另一个致命弱点。但因此说C++效率低下未免有失公允（实际上，我正在为 STLSoft 库写字符至整型的转换，以和它 <STRONG>integer_to_string<> </STRONG>的杰出转换性能相匹配。最初的结果表明它将优于 C的atoi，所以这个转换中C++也是优于C的。我希望能在本文刊发前完成此转换。请参阅 <A href="http://stlsoft.org/conversion_library.html">http://stlsoft.org/conversion_library.html </A>。 ） </P>
<P><STRONG>picalc </STRONG>在这里各语言性能表现相近。 C#和Java几乎相同，比C，C++效率高上10%。C#的性能可以这样解释：它的浮点数操作效率高，这可以从f2I看出。但这不能理解Java的表现。我们可以认为C#和Java都能较好的优化循环，它涉及函数调用，但这有赖于深入研究。既然性能相差10%以内，我们只能得出这样的结论：各语言在循环结构性能方面差异不显著。有趣的是，Intel的优化这里没有实际效果，可能是因为Pi值计算太简单了吧。 </P>
<P><STRONG>Picalcr </STRONG>: 这里的结果可以更加确定一点， C ， C++ ， C# 和 D 语言的性能偏差在 2% 之内，我们可以公平地说它们对于递归执行的性能是一样的。 Java 却是花费比其他四种语言多 20% 的时间，加上 JVM （ Java 虚拟机）在超过 4500 次递归时堆栈耗竭，可以明显地从速度和内存消耗方面得出 Java 处理递归不是很好。 Intel 的优化能力在这里有明显的作用（让人印象深刻的 10% ），但我在这里并没有详细分析这一点。 </P>
<P><STRONG>Sieve: </STRONG>相对简单的计算质数的算法（仅包含迭代和数组访问）说明 C ， C++ ， C# 和 D 语言在这方面事实上是一样的（仅 0.5% 的偏差），但 Java 的性能低了 6% 。我认为这中间大有文章， C ， C++ 代码不进行数组边界安全检查，而 C# 和 Java 进行数组边界检查。我对这种结果的解释是： C# 和 D 语言在 FOR 循环内能根据对数组边界的条件测试对边界检查进行优化，而 Java 不行。由于 C# 是新兴语言， D 语言还没发行，这就让人不那么失望了。即使这不是为 Java 辩解，也不会让人印象深刻了。再说， Intel 优化起了明显的作用——增长 5% 性能，正如 picalcr 例子，这相对于其他语言更让人印象深刻。 </P>
<P><STRONG>Strswtch </STRONG>: 这个测试对于 C# 语言不是很好。即使是 C++ 低效的字符串类操作符 = = 都比 C# 的性能快 2.5 倍 (Digital Mars) 到 5 倍（ Intel VC6 STL/CRT ）。 .NET 环境下字符串是内部处理的，这表示字符串存储在全局离散表中，副本可以从内存中消除，等值测试是以一致性检查的方式进行的 ( 只要两个参数都是在内部 ) 。这明显表示内部修复机制的效率严重低下或者一致性检查没有建立 C# 的 ”string-swatching” 机制性。 C# 的这种严重低效率更让人倾向于后者，因为想不到个好的理由解释为什么会这样。正如例 8 所示，变量 1 的字符串实例是文字的，都在内部（事实上，这里有个限制没有在例 8 中表示出来，在变量 1 的预循环中用来验证参数是真正在内部的）。 </P>
<P>当在变量 2 中假装进行一致性检查，发现仅 Java 的性能有明显的提高，从 2% 到令人印象深刻的 30% 。很明显 Java 在使用一致性检查所取的性能是不切实际的，因为在真正的程序中不能限制字符串都是文字的。但这可用来说明支持内部处理和提供可编程实现访问这种机制的语言的一种性能。讽刺的是， C# 作为这五种语言之一，它的性能是最差的。 </P>
<P><STRONG>Strcat </STRONG>: 表 2 以微秒（ μs ）显示了五种语言每一种在这个例子中的四个变量的花费时间。本文没有足够的篇幅来细节描述 17 执行代码，但尽量保持例子 7 的四个变量的真实面貌。变量 1 和 2 在所有语言执行代码中涉及以堆栈为基础的字符串内存，同时 C ， D 语言在第三个变量中、 C ， C++ ， D 语言在第四个变量中用结构内存，这在某种程度说明他们的性能优于其他变量和语言的执行代码。本例子的 C ， C++ 的讨论适合于 Digital Mars 版本，因为很明显的 Intel 所带来的性能提高被 Visual C++ 运行时刻库的效率（非期望）所抵消，事实上 Intel 的每种测试的性能都比 Digital Mars 的差。 </P>
<P>第一种测试是以默认的方式执行的，很显然 C# 和 D 语言的性能都好于其它语言。很奇怪的是 Java 的性能最差，因为 Java 被认为有能力在一条语句中把字符串序列解释成字符串构件格式（在变量 4 中手工转换）。 </P>
<P>第二种测试，众所周知它的格式不好，透露了很多东西。它的劣性根据语言而不同，对于 C# ， D 和 Java ，包含在连续的表达式中连接每一项，而不是象第一个变量在一个表达式中。对于 C ，它简单地省略了在开始多项连接重新分配之前的的内存请求。对于 C++ ，它使用 strstream 和插入操作符而不是使用 str::string 和 + （）操作符。结果显示所有语言不同程度地降低性能。 C 内存分配器的附加轮询看起来没有起多大作用，可能因为没有竞争线程造成内存碎片，所以消耗同样的内存块。 C++ 改变成 iostreams 使代码更紧凑，但没有转变数字部分，它们仅被插入到流当中，所预计的执行结果验证了这一点。 D 因为这个改变降低性能许多（ 32% ）， Java 更多（ 60% ）。很奇怪的是 C# 性能在这里仅降低很小，少于 7% 。这让人印象非常深刻，意味着可能在 .NET 的开发环境下因为 Java 字符串连接而形成的代码回查的警觉本能会衰退。 </P>
<P>第三种测试使用 printf()/Format() 的变量不足为奇。通过使用部分 / 全部的帧内存， C ， C++ 和 D 达到很好的性能。奇怪的是 C# 的性能仅是 22% ，简直不值一提。 Java 根本没有这个便利功能。（以个人的观点也是不值一提的） C#和D语言都能够在封闭的循环中，依照数组边界测试情况，优化他们的越界检查。 </P>
<P>最大的 测试 －硬编码的性能－这个是很有趣的问题，因为 C++还是能提供超级的性能，如果能够依照问题的精确领域来编码，即使别的语言（C#,java）使用默认的或者性能优化的组件或者理念也是如此。在C#和java上使用他们各自的StringBuilders能够提供真实的效果，达到他们在这个集合中的最佳性能。然而，C#能发挥最佳效率的机制，还是不能与C/C++、D语言相比。更糟糕的是，java的最佳性能都比别的语言的最差性能要可怜，这是相当可悲的。 </P>
<P>总之，我们说对底层直接操作的语言垂手可得地赢得那些转化率工作得很好的或者完全地在中间代码中，并且很显然地能够提供良好性能的语言用法对java是很重要的而对C#是不太重要的。请注意这一点。 </P>
<P><STRONG>Strtok </STRONG>. Table3显示了五种语言的每一个环节总共的时间（单位是毫秒ms）。同时他们的空间占用在这篇文章中并没有提及，但是所有语言的实现都是非常直接的并且很明显很接近在Listing9中列出的四个问题的本质。与strcat一样，基于同样的原因，使用Intel编译器的结果没有讨论。 </P>
<P>第一个问题――分割字符，保留空格――显示C++版本是明显的胜利者，D语言要慢20％，C# 要3倍的时间。显然，这个与库的特征的关系比语言的更明显， </P>
<P>并且我使用 tokenizer比著名的Boost更多一些，那样的话，D语言要比C++快2倍，并且C＃只慢20％。虽然如此，STLSoft的tokenizer是免费得到,我想我们应该把这个作为C++的一个胜利。（其实，template在这一点上的使用STL的basic_string<char>作为他的值类型，这个并不因为他的良好性能而闻名，这可能引起争论－我并没有使用一个更有效率的string，就像STLSoft的frame_string.总之，我认为这是个公平比较）。 </P>
<P>第二个问题－－分割字符，去掉空格－－包含所有语言的实现版本。自然地，C语言赢得了比赛，它使用了strtok()函数，这个函数在创建tokens时并不分配内存并且直接写终结符NULL到tokenized string的结尾。尽管有这些不利条件，C++的性能还是很好的，时间是C的221％，比较好的是D语言，432％。 </P>
<P>C＃和java就很差了，分别是746％和957％。这简直是不敢相信的，C＃和java运行的时间是C++的3.4倍和4.3倍，这三种语言都为tokens分配了内存。我相信这个是STL模型在处理iterable sequences时比passing around arrays更有效率的很好的例子。（请注意，我们有高度的信心这是场公平的关于C++,C#和D语言的比较，我写了三种tokerizer的实现，都是公开的，可得到的.) </P>
<P>第三个问题－－分割句子，保留空格--显示了两件事情。第一，在这三种语言在实现这个程序的代价比实现单个字符更昂贵。第二，我们发现D语言取代了C++的最佳性能的地位，领先5％左右。C＃继续被甩下，大概是另外两种语言的1.8倍时间左右。 </P>
<P>第四个问题－－分割句子，去掉空格--没有任何令人意外的，C++和D拥有大概一致的性能。C＃比较不错（大概2倍的性能差距）java更慢（差不多3倍时间）。对于C++，C＃和D来说去掉空格比不去掉空格导致一小点的性能损失。很明显的，在C＃中，这么做比其他两个的损失更大一点。很有价值的是，这些功能在C＃和D中是已经实现的。由于数组在C#中是不可变的，从tokens中返回一个没有空格的数组将导致重新分配一个数组。可是，D允许指派数组的长度，这个能动态地调整数组的大小这是个很好的例子说明D语言提供更好的效率。 </P>
<P>总的来说，考虑到性能和自由度，我们能够说 C++是胜利者，D是很接近的第二名。C拥有最佳的性能，但是只支持一种类型的tokerization。C＃表现得很差，java更甚。 </P>
<P><STRONG>总结 </STRONG></P>
<P>我们可以把结果分成3种情况：这些包括语言的特性，包括库的特性，或者两者都相关。这些只与语言相关的特性显示在 f2i, i2f, picalc, picalcr, and sieve scenarios,这些在语言选择上的作用是很小的。c＃看起来在总体上是最好的，但是有点让人不能信服的是它在f2i中取得的优异性能并且是因为牺牲了浮点精确度而取得效率。（这一点需要更多大检查，我将在下一篇文章中去做。） </P>
<P><STRONG>java是明显最差的 </STRONG>。 </P>
<P>当讨论intel编译器的浮点计算的深度优化效果时，它很明显地显示，c和c++能够做的比C＃更好。我讨论这些并不是说这些语言必须要更好的效率，而是说他们能比其他的语言提供更好的优化和增强的机会。这个大概是与本主题完全不相干的是，C和C++比别的语言有更多的编译器；更多的竞争，更聪明的人拥有不同的策略和技术。编译器的结果的运行目标是处理器，这将导致戏剧性的效果；我们仅仅在表面上探讨了这个庞大的问题，但是我不久将在以后的文章中回到这个主题。 </P>
<P>Strtok 是唯一一个能说是library-only的，在这一点上C＃干的并不好。虽然比java快，但是比其他的语言慢2倍或更多。同样令人失望的是VisualC/C++的运行库。在这一点上包括语言和库的效果的是i2str,str2i,strcat和strtswtch，描绘的并不是很清楚。C＃明显的在string的拼接这一环节上比java好得多，但是明显地比其他的差。关于尊敬的C，C++,D，只是在一些环节上领先，在另外一些环节上相当地差。 </P>
<P>很有趣的是定制的C++的库替换加上深度优化的intel的编译器。 </P>
<P>总之，不可能做出定量的结论。从语言的表层来看， C# 和 Java 挺简单的，但低效的库降低了速度。我觉得，当相关的库还不如语言本身的时候， C# 的确实有微弱的优势；但反过来，当库逐渐完善，超过语言本身后， C# 明显就不行了。但还是能看到一点点希望： C# 还是有可能实现预期的目标，因为库比语言更容易实现组装。但是，人们认为由于跟别的语言相比， C# 和 Java 所用的库与语言结合的更加紧密，这些库就可以作为这两种语言效率的关键所在，至少对当前的版本来说是这样的。 </P>
<P>正如我所作出的示范（从整数到字符）以及相关提到的（从字符到整数），有人可能会批评者并不符合这篇文章的原意。 </P>
<P><STRONG>更深入的探讨 </STRONG></P>
<P>本文主要探讨了一下 C# 对 C++ 和 Java 可能造成的“威胁”。总的来说，这结果虽然不怎么鼓舞人心，却足以让人吃惊。从效率上看， C# 跟 C 以及 C++ 这些过去的同类产品（假如它们是更高效的）相比只能算是一般水平，至少在基本语言特征的比较上是这样的。从某种程度上来说， Java 也是如此。（我承认我无法证明 C 和 C++ 相对于 C# 的绝对优势。在多年前我的毕业论文分析调查期间，我认识到令人乏味的结果正和令人兴奋的结果一样生动形象，但前者花费更小的经济支出。） </P>
<P>上述结论对于多处理器以及（高性能的文件／网络处理系统）等来说却不一定适用。不过，这确实很好地展现出了这些语言的基本效率以及更复杂的运算所依赖的最基本的库。 </P>
<P>就 D 发展状况来看，现在就对它进行这样的归类似乎为时过早，不过它现在的表现确实不错。 D 的效率至少是 C# 的 167% ，其实大多数情况下还不止。有些人指出，目前仅仅还处于字符时代，只需几个人就能完成一个编译器及其附带的库。我猜测， D 有可能发展成一门强大的技术。 </P>
<P>就我个人而言，作为一个对 C# 持有偏见的人，偶然还是会对它的性能感到惊讶。我热切地期盼着能将性能的对比分析延伸到更多领域：复杂而高效的内存的应用，多线程，进程通信，特殊文件处理，手写代码开销，这些技术都能让我们对语言进行更深入的研究。能看到 Inter 公司怎样通过定制的 C Library （比如 CRunTiny ；参考 http://cruntiny.org/ ）和 STL （比如 STLPort ）。 </P>
<P><STRONG>鸣谢 </STRONG></P>
<P>感谢 Walter Bright 为我提供了一份言简意赅的关于 D 的介绍，并及时地指出了一些当时被我忽略的优点，使我能对测试工程进行改进。同时也要感谢 Sun Microsystems 公司的 Gary Pennington ，他为我提供了关于 Java 方面的资料。还有 Scott Patterson ，他对本文的草稿进行了大量的精简，并对全篇文章进行了细致的检查。 <BR><BR></P>]]&gt;<img src ="http://www.blogjava.net/qq13367612/aggbug/16243.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-15 10:23 <a href="http://www.blogjava.net/qq13367612/articles/16243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈谈软件学院有哪些饭碗型关键课程</title><link>http://www.blogjava.net/qq13367612/articles/16277.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 02 Sep 2005 05:59:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/16277.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16277.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/16277.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16277.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16277.html</trackback:ping><description><![CDATA[<SPAN class=unnamed3>软件学院的专业基础课程如数据结构、操作系统、数据库原理、编译原理等不必说了，与大多数学校的计算机专业基本相同，这些专业基础课的重要性，面试时一般都会考这些基础课，此处不想再谈了。将来找工作时，只学以上基础课是绝对不够的，基础与技能必须并重！全国的IT专业基本都学这些基础课（只会这些基础课的毕业生车载斗量，不可计数，教学内容陈旧并且严重脱离实际，是当前国内IT教育存在的普遍问题，如果您也是这样，将来就业时会被淹没在茫茫人海中），我们必须有自己的独特优势，才能在激烈的竞争中脱颖而出。十分明显，企业最欢迎的是能立即解决问题的毕业生，不是纸上谈兵者。所以这里我只想说将来找工作时大家赖以为生的饭碗课程。<BR><BR>目前我院有以下五大金刚课程是专为谋生设置的，属学院的看家课程：<BR>1. J2EE Architecture and Programming (Websphere or Weblogic)<BR>2、OOAD with UML (Rational Rose)<BR>3、Windows Programming (VC++)<BR>4、Large-scaled Database (SQL Server,Oracle)<BR>5. Embedded Real-time Operating System (WinCE or Vxworks or Embedded Linux)<BR>(6. Embedded Microprocessor Architecture and Development (ARM) )<BR><BR>当然，最重要的是以上每门课程都应按教师要求认真完成一个大的课程项目（否则等于没学）。再加上软件学院的杀手锏，到IT企业实习（等于实际工作经验），将来拿着完成的一个个项目或作品去找工作和丰实的项目简历，基本上可做到攻无不克，战无不胜。<BR><BR><BR>对于开发平台，我院目前策略是主攻J2EE和VC++。对于Windows平台，因VC++是最难的也是最核心的，所以主攻VC++，若能攻下VC++，Windows平台就全好办了（往上可做GDI、ADO、Winsock、DirectX等应用，往下可做COM、Device driver、Windows CE开发等）。若大家会J2EE，Dot Net就较简单了，所以目前不将Dot Net作为重点，除非以后Dot Net有较大社会需求。<BR><BR><BR>一、企业应用系统类职位<BR><BR>温饱型饭碗（必备，可对付70%的职位，特别是大公司）：J2EE+UML+SQL<BR><BR>小康型饭碗（选项，可增加20%的职位）：温饱型+VC++（包括ADO、DirectX等）<BR><BR>富裕型饭碗（选项）：小康型+XML+Web Service+Dot Net（+设计模式）<BR><BR>特殊型饭碗：一门程序设计语言+一个大型数据库+某项特殊技术 (目前主要紧缺下列人员：IBM大型机开必技术，或者SAP技术（如ABAP编程、Basis系统管理），或者PeopleSoft技术等）。对于游戏软件人才，VC+++DirectX很重要。<BR><BR>面试必考：C、C++、Java、数据结构，一般还会考软件工程、数据库原理、操作系统、计算机网络，外加各金刚课程经验。<BR><BR>二、嵌入式系统类职位<BR><BR>温饱型饭碗（必备，可对付70%的职位）：C+C+++ARM+RTOS（+Assembly）。其中RTOS至少应掌握WinCE、Vxworks、Embedded Linux (ucLinux是目前用得最多的嵌入式Linux)中的一个，并做过这方面的开发项目。若是要学WinCE，必须掌握VC++。Vxworks和Embedded Linux，必须熟悉C，C++，一般还应知道Assembly，另外还应掌握ARM处理器结构和应用开发方法。<BR><BR>小康型饭碗：温饱型+某个嵌入式应用领域。最主要的应用领域目前主要是（1）MPEG编解码技术和DirectX编程 (2)TCP/IP协议栈或某个无线通信协议<BR><BR>富裕型饭碗：小康型+DSP软件开发技术。现在DSP软件开发人才紧缺，但开发DSP软件须学信号与系统、数字信号处理这两门电子通信类专业的基础课，否则看不懂DSP软件。<BR><BR>面试必考：C、C++、Assembly、操作系统、数据结构、硬件(数字电路、计算机组成原理等）、计算机网络等，外加某个实时操作系统经验。<BR><BR>由于应聘嵌入式职位要求较高（有时要求硕士），所以建议嵌入式系统方向的人，也应学J2EE，以防万一应聘嵌入式工作出现困难时，还可转找企业系统类工作。<BR><BR>至于课程完整的体系结构，可参考public filesOfficial Documents (学院官方资料 )教学与培养计划中的同济大学软件学院软件工程专业(四年制本科生)培养计划(2004级开始)文件，这是一个经反复研究后的一个较全面的教学计划。该计划的设想是使学生将来毕业往以下任何一个方向发展都有专业基础：企业计算、嵌入式、DSP、通信，甚至可往IC设计转，为此增加了一些硬件、电子、通信类专业基础课。<BR><BR>最后要强调，虽然软件学院的专业名称是软件工程，但这里的“软件工程”的含义已广义了，只要是IT业最需求人才的领域，都可以是软件工程专业的培养目标（例如有些软件学院在大搞IC设计），这是在2003年底教育部示范软件学院建设会上大家达成的共识。软件学院的培养特点就是紧密结合社会需求，根本不受“软件工程”几个字所制约。</SPAN><![CDATA[<SPAN class=unnamed3>软件学院的专业基础课程如数据结构、操作系统、数据库原理、编译原理等不必说了，与大多数学校的计算机专业基本相同，这些专业基础课的重要性，面试时一般都会考这些基础课，此处不想再谈了。将来找工作时，只学以上基础课是绝对不够的，基础与技能必须并重！全国的IT专业基本都学这些基础课（只会这些基础课的毕业生车载斗量，不可计数，教学内容陈旧并且严重脱离实际，是当前国内IT教育存在的普遍问题，如果您也是这样，将来就业时会被淹没在茫茫人海中），我们必须有自己的独特优势，才能在激烈的竞争中脱颖而出。十分明显，企业最欢迎的是能立即解决问题的毕业生，不是纸上谈兵者。所以这里我只想说将来找工作时大家赖以为生的饭碗课程。<BR><BR>目前我院有以下五大金刚课程是专为谋生设置的，属学院的看家课程：<BR>1. J2EE Architecture and Programming (Websphere or Weblogic)<BR>2、OOAD with UML (Rational Rose)<BR>3、Windows Programming (VC++)<BR>4、Large-scaled Database (SQL Server,Oracle)<BR>5. Embedded Real-time Operating System (WinCE or Vxworks or Embedded Linux)<BR>(6. Embedded Microprocessor Architecture and Development (ARM) )<BR><BR>当然，最重要的是以上每门课程都应按教师要求认真完成一个大的课程项目（否则等于没学）。再加上软件学院的杀手锏，到IT企业实习（等于实际工作经验），将来拿着完成的一个个项目或作品去找工作和丰实的项目简历，基本上可做到攻无不克，战无不胜。<BR><BR><BR>对于开发平台，我院目前策略是主攻J2EE和VC++。对于Windows平台，因VC++是最难的也是最核心的，所以主攻VC++，若能攻下VC++，Windows平台就全好办了（往上可做GDI、ADO、Winsock、DirectX等应用，往下可做COM、Device driver、Windows CE开发等）。若大家会J2EE，Dot Net就较简单了，所以目前不将Dot Net作为重点，除非以后Dot Net有较大社会需求。<BR><BR><BR>一、企业应用系统类职位<BR><BR>温饱型饭碗（必备，可对付70%的职位，特别是大公司）：J2EE+UML+SQL<BR><BR>小康型饭碗（选项，可增加20%的职位）：温饱型+VC++（包括ADO、DirectX等）<BR><BR>富裕型饭碗（选项）：小康型+XML+Web Service+Dot Net（+设计模式）<BR><BR>特殊型饭碗：一门程序设计语言+一个大型数据库+某项特殊技术 (目前主要紧缺下列人员：IBM大型机开必技术，或者SAP技术（如ABAP编程、Basis系统管理），或者PeopleSoft技术等）。对于游戏软件人才，VC+++DirectX很重要。<BR><BR>面试必考：C、C++、Java、数据结构，一般还会考软件工程、数据库原理、操作系统、计算机网络，外加各金刚课程经验。<BR><BR>二、嵌入式系统类职位<BR><BR>温饱型饭碗（必备，可对付70%的职位）：C+C+++ARM+RTOS（+Assembly）。其中RTOS至少应掌握WinCE、Vxworks、Embedded Linux (ucLinux是目前用得最多的嵌入式Linux)中的一个，并做过这方面的开发项目。若是要学WinCE，必须掌握VC++。Vxworks和Embedded Linux，必须熟悉C，C++，一般还应知道Assembly，另外还应掌握ARM处理器结构和应用开发方法。<BR><BR>小康型饭碗：温饱型+某个嵌入式应用领域。最主要的应用领域目前主要是（1）MPEG编解码技术和DirectX编程 (2)TCP/IP协议栈或某个无线通信协议<BR><BR>富裕型饭碗：小康型+DSP软件开发技术。现在DSP软件开发人才紧缺，但开发DSP软件须学信号与系统、数字信号处理这两门电子通信类专业的基础课，否则看不懂DSP软件。<BR><BR>面试必考：C、C++、Assembly、操作系统、数据结构、硬件(数字电路、计算机组成原理等）、计算机网络等，外加某个实时操作系统经验。<BR><BR>由于应聘嵌入式职位要求较高（有时要求硕士），所以建议嵌入式系统方向的人，也应学J2EE，以防万一应聘嵌入式工作出现困难时，还可转找企业系统类工作。<BR><BR>至于课程完整的体系结构，可参考public filesOfficial Documents (学院官方资料 )教学与培养计划中的同济大学软件学院软件工程专业(四年制本科生)培养计划(2004级开始)文件，这是一个经反复研究后的一个较全面的教学计划。该计划的设想是使学生将来毕业往以下任何一个方向发展都有专业基础：企业计算、嵌入式、DSP、通信，甚至可往IC设计转，为此增加了一些硬件、电子、通信类专业基础课。<BR><BR>最后要强调，虽然软件学院的专业名称是软件工程，但这里的“软件工程”的含义已广义了，只要是IT业最需求人才的领域，都可以是软件工程专业的培养目标（例如有些软件学院在大搞IC设计），这是在2003年底教育部示范软件学院建设会上大家达成的共识。软件学院的培养特点就是紧密结合社会需求，根本不受“软件工程”几个字所制约。</SPAN>]]&gt;<img src ="http://www.blogjava.net/qq13367612/aggbug/16277.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-02 13:59 <a href="http://www.blogjava.net/qq13367612/articles/16277.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows 2000 常用命令行</title><link>http://www.blogjava.net/qq13367612/articles/15993.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Thu, 01 Sep 2005 08:47:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/articles/15993.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/15993.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/articles/15993.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/15993.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/15993.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 网络经典命令行 -> Windows 2k/2003 Server1.最基本，最常用的，测试物理网络的 ping 192.168.10.88 －t ，参数－t是等待用户去中断测试  2.查看DNS、IP、Mac等 A.Win98：winipcfg B.Win2000以上：Ipconfig/all  C.NSLOOKUP：如查看河北的DNS C:\>nslooku...&nbsp;&nbsp;<a href='http://www.blogjava.net/qq13367612/articles/15993.html'>阅读全文</a><img src ="http://www.blogjava.net/qq13367612/aggbug/15993.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-09-01 16:47 <a href="http://www.blogjava.net/qq13367612/articles/15993.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>