??xml version="1.0" encoding="utf-8" standalone="yes"?>久久天天日天天操综合伊人av,色综合久久精品中文字幕首页,久久久无码精品亚洲日韩按摩http://www.shnenglu.com/ace/category/1419.html<table style="border:1px solid #aa0033; font-size:small" align=center> <tr> <td rowspan=3> </td> <td colspan=2 align=center><b>订阅 ace-china</b></td> </tr> <form action="http://groups.google.com/group/ace-china/boxsubscribe"> <input type=hidden name="hl" value="zh-CN"> <tr> <td>电子邮gQ?<input type=text name=email></td> <td> <table style="background-color:#ffcc33;padding:2px;border:2px outset #ffcc33;"> <tr> <td> <input type=submit name="sub" value="订阅"> </td> </tr> </table> </td> </tr> </form> <tr><td colspan=2 align=center> 览存于 <a >groups.google.com</a> 上的<a >所有帖?lt;/a> </td></tr> </table>zh-cnThu, 22 May 2008 05:18:47 GMTThu, 22 May 2008 05:18:47 GMT60Ƣ迎讉Kwww.ace-tao.org/bbshttp://www.shnenglu.com/ace/archive/2007/12/05/37843.htmlStone JiangStone JiangWed, 05 Dec 2007 04:59:00 GMThttp://www.shnenglu.com/ace/archive/2007/12/05/37843.htmlhttp://www.shnenglu.com/ace/comments/37843.htmlhttp://www.shnenglu.com/ace/archive/2007/12/05/37843.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/37843.htmlhttp://www.shnenglu.com/ace/services/trackbacks/37843.htmlwww.ace-tao.org/bbs




Stone Jiang 2007-12-05 12:59 发表评论
]]>
使用vs2005(vc8)~译log4cpp-0.3.5rc3http://www.shnenglu.com/ace/archive/2007/01/27/18050.htmlStone JiangStone JiangSat, 27 Jan 2007 08:38:00 GMThttp://www.shnenglu.com/ace/archive/2007/01/27/18050.htmlhttp://www.shnenglu.com/ace/comments/18050.htmlhttp://www.shnenglu.com/ace/archive/2007/01/27/18050.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/18050.htmlhttp://www.shnenglu.com/ace/services/trackbacks/18050.html

使用vs2005(vc8)~译log4cpp-0.3.5rc3

问题Q由于log4cpp-0.3.5rc3仅提供了vc6的工E文Ӟ因此Q用vs2005打开后,需要进行{换。但是{换后Q不能正编译,提示Custom Build Step时出C错误?/p>

分析Q因为log4cpp在生成NTEventLogAppender.dllӞ需要连接NTEventLogCategories.mc文g。所以,目讄了自定义的生成步骤去生成NTEventLogAppender.dll。但从vc6的工E文件{换时Q这些步骤却没有正确的{换过来。从而出Cq问题?/p>

解决ҎQ重新填写Custom Build StepV?/p>

其中QCommandLine填写以下内容Q?/p>

if not exist $(OutDir) md $(OutDir)
"mc.exe" -h $(OutDir) -r $(OutDir) $(SolutionDir)NTEventLogCategories.mc
"RC.exe" -r -fo $(OutDir)\$(InputName).res $(ProjectDir)\$(InputName).rc
"link.exe" /MACHINE:IX86 -dll -noentry -out:$(OutDir)\NTEventLogAppender.dll $(OutDir)\$(InputName).res

Outputs填写Q?(OutDir)\NTEventLogAppender.dll

适用范围Qlog4cpp目、log4cppDLL目的Debug和Release配置。同Ӟ该方法适用于vs2003(vc7.1)?/p>



Stone Jiang 2007-01-27 16:38 发表评论
]]>
面向对象vsZ对象http://www.shnenglu.com/ace/archive/2006/06/25/8985.htmlStone JiangStone JiangSun, 25 Jun 2006 08:18:00 GMThttp://www.shnenglu.com/ace/archive/2006/06/25/8985.htmlhttp://www.shnenglu.com/ace/comments/8985.htmlhttp://www.shnenglu.com/ace/archive/2006/06/25/8985.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/8985.htmlhttp://www.shnenglu.com/ace/services/trackbacks/8985.html面向对象vsZ对象
看到了一U说法,很多所谓面向对象开发只不过是基于对象的开?wbr>。反思自w,发觉一针见血?/wbr>
我们所谓的面向对象Q实际上只不q封装了业务对象Q分Z层次Q提供了接口。虽然在设计和实现的时候用到了面向对象的装Q但现在看来q算不上真正的面向对象,只能是Z对象而已?/wbr>
面向对象的原则ƈ没有W合。面向对象的优势也没有发挥,反而感觉一个小变化q扯到多层的变动,ȝ。就好比d的项目,数据库采用ORACLEQ实际本w对ORACLEq不熟悉Q只l过不到一周的学习Q就上马了。结果只不过ORACLE当成MSSQL使用。ORACLE的强大不但没有体玎ͼ到现在也没有搞明白)Q反而失MMSSQL的好的经验。得不偿失?
不过Q如何才真正的面向对象呢?想了想一个好的面向对象的实践Q应该是辑ֈ最大限度的软g复用Qƈ且可以快速适应不断变化的需求,而对已有的部分媄响最?wbr>?/wbr>
q不是一y而就的事情,如果想真正的用项目来实践的话Q正的步骤也许应该是:
首先采用本n较熟悉的开发方法(Z对象或是面向q程Q满业务需?wbr>。这一步很关键Q只有有了这样一个原型才可以验证寚w求的满E度。个人感觉直接面向对象会陷入设计q度Qƈ且在前期耗费大量?wbr>_֊。更重要的是使用不熟悉的技术会带来风险Q得不偿失?/wbr>
然后逐渐的将采用真正的面向对象的Ҏ现有的原型q行重构。目的是提高性能Q灵zL?wbr>Q扩展性,可复用性等{,q是一个长期的不断q代的过E。可以参看经典的《重?改善代码质量》?/wbr>
最后一步是架构的提点{将pȝ中的可以外部复用的部分提炼出来Ş?wbr>一个可以复用的框架架构。有了它Q以后的开发相会事半功?wbr>。此步骤会用到重构和设计模式。整个的q程除了W一步和面向对象无大关系外,后两步则完全的面?wbr>对象的思想?/wbr>
在此之前L希望先有一个设计良好的架构Q一直这样不断的学习和进行实c结果却不尽人意。经验说明这样做实有些本末倒置。正如模式的使用一?wbr>Q在动机不明的情况下盲目的套用模式是没有意义的?/wbr>
每一个开发h员的脑袋中有一些非常美妙的创意Q这些创意没有实现的一个原因就是,太过xl节。对于面向对象还是基于对象开发,对于实现需求,完成目的初期阶D,真的无所谓。当需要考虑提高开发和l护效率的时候,面向对象成了很不错的选择?/div>

Stone Jiang 2006-06-25 16:18 发表评论
]]>思维导图L规则及徏?/title><link>http://www.shnenglu.com/ace/archive/2006/06/07/8242.html</link><dc:creator>Stone Jiang</dc:creator><author>Stone Jiang</author><pubDate>Wed, 07 Jun 2006 05:14:00 GMT</pubDate><guid>http://www.shnenglu.com/ace/archive/2006/06/07/8242.html</guid><wfw:comment>http://www.shnenglu.com/ace/comments/8242.html</wfw:comment><comments>http://www.shnenglu.com/ace/archive/2006/06/07/8242.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ace/comments/commentRss/8242.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ace/services/trackbacks/8242.html</trackback:ping><description><![CDATA[在网上看?挺好?转过来了.<br /><br /><img src="http://www.28star.com/blog/uploads/200512/26_154917_mindmanager.jpg" /><br /><img src ="http://www.shnenglu.com/ace/aggbug/8242.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ace/" target="_blank">Stone Jiang</a> 2006-06-07 13:14 <a href="http://www.shnenglu.com/ace/archive/2006/06/07/8242.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Rational Rose中用UML建模时的元素重蝲http://www.shnenglu.com/ace/archive/2006/05/23/7535.htmlStone JiangStone JiangTue, 23 May 2006 07:26:00 GMThttp://www.shnenglu.com/ace/archive/2006/05/23/7535.htmlhttp://www.shnenglu.com/ace/comments/7535.htmlhttp://www.shnenglu.com/ace/archive/2006/05/23/7535.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/7535.htmlhttp://www.shnenglu.com/ace/services/trackbacks/7535.html在Rational Rose中用UML建模时的元素重蝲

在利用Rational Rose对系l徏模时Q可能会遇到同一个被建模的对象在不同包中h不同职责的问题?br />比如Q“用户”这个对象,在业务领域模型中Q它是一个业务工人或业务主角(business actor)Q在pȝ用例模型中,我们需要创建另一个代表系l执行者(Actor)的元素。Rational Rose以名U标识不同的模型元素Q似囑ֆ创徏一个叫"用户"的Actor则会成重名?br />q各重名Q可以称为元素“重载”?br />
下面介绍如何q行“元素重载?br />

不在同一个包内的参与者、用例、类、构件和包,名称可以相同。不同的模型元素拥有相同的名U时Q这些元素被UCؓ“重载?br />重蝲允许你进行基于多语言构g的开发?br />重蝲允许用例视图中的参与者和逻辑视图中的cL有相同的名称


创徏一个重载元?br />1Q从工具׃创徏一个新的元?br />2Q双L元素Q或者单击Browse>SpecificationQ打开规范H口
3Q在名称字段中输入名U?br />4Q单击OK按钮 

如果在其它包中存在具有相同名U的设计元素Q系l会提示你,此时点“确定”即?br />



Stone Jiang 2006-05-23 15:26 发表评论
]]>
C/C++ 内存理 Heap vs Stackhttp://www.shnenglu.com/ace/archive/2006/05/17/7313.htmlStone JiangStone JiangWed, 17 May 2006 09:09:00 GMThttp://www.shnenglu.com/ace/archive/2006/05/17/7313.htmlhttp://www.shnenglu.com/ace/comments/7313.htmlhttp://www.shnenglu.com/ace/archive/2006/05/17/7313.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/7313.htmlhttp://www.shnenglu.com/ace/services/trackbacks/7313.html 转注:
    今天有网友问到这个问?于是在网上搜索了一?转脓q此.
怼的太多了,出处不详.在此感谢原作者精彩讲?


一、预备知识—程序的内存分配
一个由c/C++~译的程序占用的内存分ؓ以下几个部分
1、栈区(stackQ— 由~译器自动分配释放 ,存放函数的参数|局部变量的值等。其操作方式cM于数据结构中的栈?
2、堆区(heapQ — 一般由E序员分配释放, 若程序员不释放,E序l束时可能由OS回收 。注意它与数据结构中的堆是两回事Q分配方式倒是cM于链表,呵呵?
3、全局区(静态区Q(staticQ—,全局变量和静态变量的存储是放在一块的Q初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在盔R的另一块区域。? E序l束后有pȝ释放
4、文字常量区 —常量字W串是攑֜q里的。 程序结束后ql释?
5、程序代码区—存攑ևC的二q制代码?

二、例子程?
q是一个前辈写的,非常详细

//main.cpp 
int a = 0; 全局初始化区 
char *p1; 全局未初始化区?br />main() 

int b;// 栈?/span>
char s[] = "abc"//栈?/span>
char *p2; //栈?/span>
char *p3 = "123456"123456\0";//在常量区Qp3在栈上。?/span>
static int c =0Q?/span>//全局Q静态)初始化区 
p1 = (char *)malloc(10); 
p2 
= (char *)malloc(20); 
//分配得来?0?0字节的区域就在堆区。?/span>
strcpy(p1, "123456"); //123456\0攑֜帔R区,~译器可能会它与p3所指向?123456"优化成一个地斏V?/span>
}
  

三、堆和栈的理论知?
3.1甌方式
stack:
ql自动分配。 例如,声明在函C一个局部变量 int b; pȝ自动在栈中ؓb开辟空?
heap:
需要程序员自己甌Qƈ指明大小Q在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用newq算W?
如p2 = (char *)malloc(10);
但是注意p1、p2本n是在栈中的?
3.2 甌后系l的响应
栈:只要栈的剩余I间大于所甌I间Q系l将为程序提供内存,否则报异常提示栈溢出?
堆:首先应该知道操作pȝ有一个记录空闲内存地址的链表,当系l收到程序的甌Ӟ
会遍历该链表Q寻扄一个空间大于所甌I间的堆l点Q然后将该结点从I闲l点链表中删除,q将该结点的I间分配l程序,另外Q对于大多数pȝQ会在这块内存空间中的首地址处记录本ơ分配的大小Q这P代码中的delete语句才能正确的释放本内存I间。另外,׃扑ֈ的堆l点的大不一定正好等于申L大小Q系l会自动的将多余的那部分重新攑օI闲链表中?
3.3甌大小的限?
栈:在Windows?栈是向低地址扩展的数据结构,是一块连l的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是pȝ预先规定好的Q在 WINDOWS下,栈的大小?MQ也有的说是1MQM是一个编译时q定的常数Q,如果甌的空间超q栈的剩余空间时Q将提示overflow。因此,能从栈获得的I间较小?
堆:堆是向高地址扩展的数据结构,是不q箋的内存区域。这是由于系l是用链表来存储的空闲内存地址的,自然是不q箋的,而链表的遍历方向是由低地址向高地址。堆的大受限于计算机系l中有效的虚拟内存。由此可见,堆获得的I间比较灉|Q也比较大?
3.4甌效率的比较:
栈由pȝ自动分配Q速度较快。但E序员是无法控制的?
堆是由new分配的内存,一般速度比较慢,而且Ҏ产生内存片,不过用v来最方便.
另外Q在WINDOWS下,最好的方式是用VirtualAlloc分配内存Q他不是在堆Q也不是在栈是直接在q程的地址I间中保留一快内存,虽然用v来最不方ѝ但是速度快,也最灉|
3.5堆和栈中的存储内?
栈: 在函数调用时Q第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句Q的地址Q然后是函数的各个参敎ͼ在大多数的C~译器中Q参数是由右往左入栈的Q然后是函数中的局部变量。注意静态变量是不入栈的?
当本ơ函数调用结束后Q局部变量先出栈Q然后是参数Q最后栈指针指向最开始存的地址Q也是dC的下一条指令,E序p点l运行?
堆:一般是在堆的头部用一个字节存攑֠的大。堆中的具体内容有程序员安排?
3.6存取效率的比?

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在q行时刻赋值的Q?
而bbbbbbbbbbb是在~译时就定的;
但是Q在以后的存取中Q在栈上的数l比指针所指向的字W串(例如?快?
比如Q?
#include <stdio.h>
void main() 

char a = 1
char c[] = "1234567890"
char *="1234567890"
= c[1]; 
= p[1]; 
return

对应的汇~代?
10: a = c[1]; 
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 88 4D FC mov byte ptr [ebp-4],cl 
11: a = p[1]; 
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A 42 01 mov al,byte ptr [edx+1] 
00401073 88 45 FC mov byte ptr [ebp-4],al 

W一U在d时直接就把字W串中的元素d寄存器cl中,而第二种则要先把指针D到edx中,在根据edxd字符Q显然慢了?


3.7结Q?
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜Q发出申P、付钱、和吃(使用Q,吃饱了就赎ͼ不必理会切菜、洗菜等准备工作和洗、刷锅等扫尾工作Q他的好处是快捷Q但是自由度?
使用堆就象是自己动手做喜Ƣ吃的菜_比较ȝQ但是比较符合自q口味Q而且自由度大?

堆和栈的区别主要分:
操作pȝ斚w的堆和栈Q如上面说的那些Q不多说了?
q有是数据l构斚w的堆和栈Q这些都是不同的概念。这里的堆实际上指的是Q满_性质的)优先队列的一U数据结构,W?个元素有最高的优先权;栈实际上是满先进后出的性质的数学或数据l构?
虽然堆栈Q堆栈的说法是连h叫,但是他们q是有很大区别的Q连着叫只是由于历史的原因?br />


Stone Jiang 2006-05-17 17:09 发表评论
]]>
4+1视图q?/title><link>http://www.shnenglu.com/ace/archive/2006/05/08/6748.html</link><dc:creator>Stone Jiang</dc:creator><author>Stone Jiang</author><pubDate>Mon, 08 May 2006 07:22:00 GMT</pubDate><guid>http://www.shnenglu.com/ace/archive/2006/05/08/6748.html</guid><wfw:comment>http://www.shnenglu.com/ace/comments/6748.html</wfw:comment><comments>http://www.shnenglu.com/ace/archive/2006/05/08/6748.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ace/comments/commentRss/6748.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ace/services/trackbacks/6748.html</trackback:ping><description><![CDATA[<p>4+1视图<br />url: http://www.shnenglu.com/ace<br /><br />用例视图:对系l功能性需求对模,描述pȝ的行为,揭示pȝ“是什么?/p> <p>逻辑视图:描述pȝ设计模型Q包含与pȝl构最重要意义的部分,比如Q系l分解成为的子系l,子系l分解成多个c,以及q些元素的职责,关系Q操作和属?/p> <p>q程视图Q描q系l分解成U程及进E的q程Q描q线E(q程Q通讯模试{?/p> <p>部v视图Q描q运行系l的物理gQ包含网l)配置Q说明每个节点的计算机,CPUQ操作系l以及它们互联的情况Q还要包括进E到节点之间的映关pR?/p> <p>实施视图Q描q系l在构徏时的分解后的子系l(包)的情况,特别是包括哪些组成部分由哪些团队开发,以及购入Q外包,开发进度等内容。项目经理应该对q个视图最感兴?br /></p><img src ="http://www.shnenglu.com/ace/aggbug/6748.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ace/" target="_blank">Stone Jiang</a> 2006-05-08 15:22 <a href="http://www.shnenglu.com/ace/archive/2006/05/08/6748.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谁应当对用例有兴?http://www.shnenglu.com/ace/archive/2006/05/08/6742.htmlStone JiangStone JiangMon, 08 May 2006 05:46:00 GMThttp://www.shnenglu.com/ace/archive/2006/05/08/6742.htmlhttp://www.shnenglu.com/ace/comments/6742.htmlhttp://www.shnenglu.com/ace/archive/2006/05/08/6742.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6742.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6742.htmlWho should be interested in Use case?

Customers, who need to be sure that the system that is getting built is the one that they want.
客户Q他们需要确定即构建的pȝ是他们需要的pȝ?/p>

Managers, who need to have an overall understanding of what the system will do in order to effectively plan and monitor the project.

l理Q他们需要对着手的pȝ有一个全局的了解,以便于对目有效的计划和监控?/p>

Analysts, who need to describe and document what the system is going to do.
分析员,他们需要对要着手的pȝq行描述和文档化?/p>

Developers, who need to understand what the system needs to do in order to develop it.
开发h员,他们需要理解系l以便于开发它?/p>

TesterQ?who need to know what the system is supposed to do so that  they can verify that is does it.
试人员Q他们需要知道期望系l实现的功能Q以便于它们验证pȝ是执行这些功能?/p>

Technical writers,who need to know what the system is supposed to so that they can describe it.
支持文档撰写者,他们需求要知道期望的系l以例描q它?br /> 
User-experience designers, who need to understand the users' goals and how the will use the system to achieve these goals.

用户体验设计人员Q他们需要理解用L目标和他们将怎样使用pȝ辑ֈ他们的目标?/p>

Stone Jiang 2006-05-08 13:46 发表评论
]]>
避免依赖的消息处理方?(试译)http://www.shnenglu.com/ace/archive/2006/05/04/6629.htmlStone JiangStone JiangThu, 04 May 2006 12:52:00 GMThttp://www.shnenglu.com/ace/archive/2006/05/04/6629.htmlhttp://www.shnenglu.com/ace/comments/6629.htmlhttp://www.shnenglu.com/ace/archive/2006/05/04/6629.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6629.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6629.html避免依赖的消息处理方?br />
Anthony Williams
url: http://www.ddj.com/dept/cpp/184429055
译? Stone Jiang
译者说明:本hq在学习英文的过E中Q有些句子很难译Q这里给出原文的链接Q欢q就其中译得不准的地方与我交换意见?br />

在您l护安全cd和避免集成电路般函数?你可以用C++的强大的力量q行消息传递?/p>

Anthony是Just Software Solution有限公司的一位Y件开发者和执行理者。可以通过anthony@justsoftwaresolutions.co.uk与之联系?/p>

使用通用的消息传递方式传递数据在C++E序中很普遍。这U技术经常用于在U程间以及从/到GUIlg间传递数据。但是消息传递仍然很隑֮现得良好Q这是因为在常见的消息传递方式中Q暴露出了过多的藕合、缺类型安全和集成电\般的消息处理函数?/p>

在本文中Q我提出了一U技术,q种技术利用C++的强大力量来避免上述~陷——在消息传递中避免不适当的藕合,l护cd安全Q以及消除集成电路般的消息处理函? The only translation units that need to known the details of a message are those containning the source and handler functions for that specific message type.) 需要{换的单元Q即需要知道的消息详细内容是包含了特定消息的类型的源代码和处理函数?/p>

传统技?/h2>


大概应用得最为广泛的消息传递技术是使用一个带有特D成员来表示消息cd的结构体Q该消息cd是消息的标识。这U方式被q泛应用归咎于用了ZC的APIQ比如X11和Microsoft Windows。在q种Ҏ中,消息l构体中要么有一个通用的字体用于区别不同消息的意义Q这个字D可被所有消息重用,或者它是更大结构的W一个成员,它的cdq型代码来定。Windows API使用前面的技术,而X11使用后面的方法。无论用哪种方式Q处理消息的代码都须查类型编码,用以军_怎么处理该消息?/p>

q些技术的问题?~Zcd安全Q集成电路般的处理函敎ͼ需要管理类型编码来保消息唯一性的适当层次。特别的Q缺乏类型安全意味着使用之前Q用代码必L消息数据转换成适当的类型。这一步是极易出错的,其在当复制和粘贴代码时(q种非常的手D常发生在ؓ处理怼消息~写代码的时?Q编译器不会在这U错误给ZQ何警告?/p>

~Zcd安全q有一个额外的问题——即它不可能单有效的通过消息pȝ传递资源或变长的数据, q是因ؓ消息的发送方L不能知道何时Q或是否Q该消息已被处理q了?/p>

在这部分Q集成电路般的消息处理函数是必须用于定消息cd的物,通过已接收的消息来消息类型,然后得到如何处理它的方式。这U处理函数往往实现Z个很大的switch语句或是一串if eles if。一些框Ӟ如MFCQ提供一些宏来减pU问题的影响Q它q不能完全消除这个问题?/p>

最后的问题是管理类型代码。它必须要求接收消息代码清楚地知道是哪一个消息,以便于正的处理它。所以,cd代码需要在处理它的相关代码中确保唯一性。比如,在Windows API中,指定范围的消息类型在不同的应用程序中代表不同的意义,q且Q在同一个应qE序中,其它范围的消息类型在不同H口或GUIlg中代表不同的意义?通常,需要所有类型代码的列表Q该列表要求在给定的范围中保持唯一Q以便于查它们的唯一性。列表常常是以头文g的Ş式给出,头文件中定义了类型代码,包含在需要知道消息类型的所有地斏V这U方式容易导致应用程序不同部分之间的藕合,而这些部分之间却没有M关系。由于这U过度的藕,单的变更Dq多的重新编译?/p>

面向对象技?br />
对象技术的一个常见特征是所有相x息类z自一个通用的基cR该特征用编译器能认识的真实cd代替了显式的cd代码。不仅如此,它还有了一个重要的Q超C风格技术的优点——类型安全。它提供的通用基类的析构函数是虚函敎ͼ所以派生的消息c能自由地管理资源,如变长的数据Q这些数据可以在析构函数中释放。仅有的需求是接受消息的代码能正确地销毁消息对象,无论它们是否被处理?/p>

理cd代码现在被替换ؓ理cR这是一个更加简单的dQ由于可能的消息名字的范围是没有限制的,可能存在名字冲突Q但q一点可以通过名字I间来解冟?/p>

保持?/font>

最单的OOP技术就是用dynamic_cast查实际的消息cd代替查消息编码。然而,q依焉临着集成电\般地消息处理方式——现在通过包括dynamic_cast的比较链也优于通过cd~码字段比较链。如列表1:

void  handleMessage(Message *  message)
{
    
if (Message1 *  m = dynamic_cast < Message1 *> (message))
    
{
        handleMessage1(m);
    }

    
else   if (Message2 *  m = dynamic_cast < Message2 *> (message))
    
{
        handleMessage2(m);
    }

    
//  
}

[列表1]

一般而言Q由于仅仅是消息的源代码和接受消息的源代码需求知道相关的消息Q所以依赖得到降低。然后,集成电\般地处理函数现在需要知道消息的有关l节Q所以dynamic_cast需要消息的完整定义——如果分z另外的函数处理实际的消息QC风格技术的处理函数不需求知道消息的l节?/p>

双重分派

(Direct testing of a class's type using dynamic_cast is generally indicative of a design problem;)cȝcd用dynamic_cast的直试一般可表示计问?然而,单地把虚函数攑֜消息cM起不CQ何作用——它把消息处理与消息缠l在一Pq个消息使在W一个地方发送消息的目的p|?/p>

双重分派的关键点是,在消息类中的虚函数带有一个作为参数的处理器,然后在处理器上把自已作ؓ参数传递传递给另一个函数ƈ完成调用。因里的W二ơ到处理器的回调已经在实际的zcM完成Q所以真实的消息cd已经知道Q在处理器上能调用适当的函敎ͼ无论q个函数是通过重蝲的方式实现还是另外独立命名的函数来实?列表2)?/p>

class  Message
{
public :
    
virtual   void  dispatch(MessageHandler *  handler) = 0 ;
};
class  Message1:
    
public  Message
{
    
void  dispatch(MessageHandler *  handler)
    {
        handler
-> process( this );
    }
};
class  Message2:
    
public  Message
{
    
void  dispatch(MessageHandler *  handler)
    {
        handler
-> process( this );
    }
};
//  other message classes
class  MessageHandler
{
    
void  process(Message1 * );
    
void  process(Message2 * );
    
//  overloads of process for other messages
};

[列表2]

依赖于重载的方式来区别不同的消息有利于大多数q——现在在每个消息cM虚函数的实现方式是相同的Q如果需要,可以通过宏来一致地包装Q或通过从一个消息到另一个消息中直接复制Q不会有出错的机会?/p>

双重分派存在一个缺点——高度藕合。由于通过重蝲方式在处理器cM的选择处理函数Q在消息cM虚函数的实现需要知道处理器cȝ定义的全部,因此必须注意到在pȝ中每个其它的cȝ名字。不光这些,如果要支持不同的处理器类Q处理函数必d通用的处理器的基cM声明函数Q所以每个处理器cddpȝ中注意到所有的消息cd(列表3)。增加或删除一个消息类型会引v应用E序大部分代码重新编译?/p>

class  MessageHandler
{
    
virtual   void  process(Message1 * ) = 0 ;
    
virtual   void  process(Message2 * ) = 0 ;
    
virtual   void  process(Message3 * ) = 0 ;
    
virtual   void  process(Message4 * ) = 0 ;
    
//  overloads of process for other messages
}
;
class  SpecificMessageHandler:
    
public  MessageHandler
{
    
void  process(Message1 * );
    
void  process(Message2 * );
    
void  process(Message3 * );
    
void  process(Message4 * );
    
//  overloads of process for other messages
}
;
class  OtherSpecificMessageHandler:
    
public  MessageHandler
{
    
void  process(Message1 * );
    
void  process(Message2 * );
    
void  process(Message3 * );
    
void  process(Message4 * );
    
//  overloads of process for other messages
}
;

[列表3]

动态双重分z?br />
QIt was against this backdrop that I developed the technique I call "Dynamic Double Dispatch."Q我开发了一U技术,我称其ؓ“动态双重分z䏀,q种技术用于解决上q问题。尽有基本的双重分z技术,但选择的消息处理函C用的是在~译阶段定的重载技?管发现在正的消息处理器类中的实现是用虚函数机制),而动态双重分z是在运行时查在处理器上适当的处理函数的。结论是动态双重分z消除了双重分派的依赖问题。消息类型不在需要注意到其它的消息类型,q且处理器类仅需要注意到它的它要处理的消息?/p>

动态检查的关键ҎQ每一个消息类型有一个独立的基类——处理器cM适当的,设计为处理消息的基类z。然后在每个消息cM的分zև数能用dynamic_cast来检查从正派基类z的处理器c,因而实C正确的处理函数?列表4)

class  MessageHandlerBase
{};
class  Message1HandlerBase:
    
public   virtual  MessageHandlerBase
{
    
virtual   void  process(Message1 * ) = 0 ;
};
class  Message1
{
    
void  dispatch(MessageHandlerBase *  handler)
    {
        dynamic_cast
< Message1HandlerBase &> ( * handler).process( this );
    }
};
class  Message2HandlerBase:
    
public   virtual  MessageHandlerBase
{
    
virtual   void  process(Message2 * ) = 0 ;
};
class  Message2:
    
public  MessageBase
{
    
void  dispatch(MessageHandlerBase *  handler)
    {
        dynamic_cast
< Message2HandlerBase &> ( * handler).process( this );
    }
};
//  
class  SpecificMessageHandler:
    
public  Message1HandlerBase,
    
public  Message2HandlerBase
{
    
void  process(Message1 * );
    
void  process(Message2 * );
};
class  OtherSpecificMessageHandler:
    
public  Message3HandlerBase,
    
public  Message4HandlerBase
{
    
void  process(Message3 * );
    
void  process(Message4 * );
};

[列表4]

(Of course, having a completely separate handler base class for each message type would add excessive complication, as the dispatch function for each message type would now be specific to that message type, and the base classes would have to be written separately, despite being fundamentally the same, except for the message type they referenced.)
诚然Qؓ每个消息cd分别~写的处理器基类增加过多的复杂性,同样圎ͼ每个消息cd各自的分zև数现在需要特别指定,基类也需求分别编?然后除了它们引用的消息类型外基础是相同的。消除这U重复的关键是基类成ؓ模板Q用消息cd作ؓ模板参数——分zև数引用到模板的实现好于指定类型;L列表5?/p>

 

template < typename MessageType >
class  MessageHandler:
    
public   virtual  MessageHandlerBase
{
    
virtual   void  process(MessageType * ) = 0 ;
};
class  Message1
{
    
void  dispatch(MessageHandlerBase *  handler)
    {
        dynamic_cast
< MessageHandler < Message1 >&> ( * handler).process( this );
    }
};
class  SpecificMessageHandler:
    
public  MessageHandler < Message1 > ,
    
public  MessageHandler < Message2 >
{
    
void  process(Message1 * );
    
void  process(Message2 * );
};

[列表5]
Z化原因,在消息类中的分派函数几乎相同Q但也不是完全相同——它们必L的指定属于它们的指定消息类Q以便于转换为适当的处理器基类。像软g中许多事情一Pq个问题可以增加一个额外的层来解决——分zև数可以委托给单个模板函数Q这个模板函C用模板参数类型来定消息cd和把处理器{换到适当的类型上?列表6)

 

class  Message
{
protected :
    template
< typename MessageType >
    
void  dynamicDispatch(MessageHandlerBase *  handler,MessageType *  self)
    {
        dynamic_cast
< MessageHandler < MessageType >&> ( * handler).process(self);
    }
};
class  Message1:
    
public  MessageBase
{
    
void  dispatch(MessageHandlerBase *  handler)
    {
        dynamicDispatch(handler,
this );
    }
};

[列表6]

通过q一步抽象在消息对象中分zև数的不同之处,我们把工作集中到一个地方——模板函数的定义Q它提供了ؓ修改行ؓ的单一炏V在消息cM剩下的分zև数都是相同的Q这以把它们简化到隐藏l节的宏中或在消息类之间中逐字复制?/p>



未处理的消息

q今为止Q我们展C的 dynamicDispach模板函数的代码假定处理的cL从适当的SpecificMessageHandler是派生的Q如是不是这P dynamic_cast抛出std::bad_cast异常。有时这p够了Q但是有的时候,有更适当的行为——也许更好的做法是抛弃消息,q不能被接受消息的代理处理或调用catch-all处理器。D例来_dynamicDispatch 函数能被调整Q用Z指针的{换代替基于引用的转换Q所以结果值可以与NULLq行试?/p>


~点(Trade-Off)在哪里?
有如此多的优点,一定存在它的缺点,那它的缺点在哪里呢?在这里,有两个缺炏V第一个是Q额外的动态{换,两个虚函数调用会影响性能。如果性能上是一个问题,q就是一个疑问,但是Q在很多情况下,花销在这里的额外的时间是不值得x的。可以用相应的工具来签定到底哪里才是真正的性能瓉所在?/p>

W二个缺Ҏ:需要ؓ每个消息处理从指定的基类z消息处理器。因为处理新的消息类型需要修改两个地方——适当的基cd表入口和处理函数Q所以这可能成ؓ错误的来源,遗失处理函数Ҏ被发玎ͼ因ؓq是全局点,但是遗失基类在代码运行时只生不易查觉的~陷。因为没有处理函数的时候仅仅是不调用它。这些错误在单元试的时候是很容易被抓出来的Q所以所实话Q这些不便之处都成不了大问题?/p>

 



Stone Jiang 2006-05-04 20:52 发表评论
]]>
[转]试驱动开发全功略http://www.shnenglu.com/ace/archive/2006/04/28/6415.htmlStone JiangStone JiangFri, 28 Apr 2006 05:03:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/28/6415.htmlhttp://www.shnenglu.com/ace/comments/6415.htmlhttp://www.shnenglu.com/ace/archive/2006/04/28/6415.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6415.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6415.html [转]试驱动开发全功略

From: Brian Sun @ 爬树的泡[http://www.blogjava.net/briansun]

{关键字}

试驱动开?Test Driven Development/TDD
试用例/TestCase/TC
设计/Design
重构/Refactoring

{TDD的目标}

Clean Code That Works

q句话的含义是,事实上我们只做两件事情:让代码奏效(WorkQ和让代码洁净QCleanQ,前者是把事情做对,后者是把事情做好。想想看Q其实我们^时所做的所有工作,除去无用的工作和错误的工作以外,真正正确的工作,q且是真正有意义的工作,其实也就只有两大c:增加功能和提升设计,而TDD 正是在这个原则上产生的。如果您的工作ƈ非我们想象的q样Q(q意味着您还存在W三cL有意义的工作,或者您所要做的根本和我们在说的是两回事)Q那么这告诉我们您ƈ不需要TDDQ或者不适用TDD。而如果我们偶然猜对(q对于我来说是偶Ӟ而对于Kent Beck和Martin Fowlerq样的大师来说则是辛勤工作的成果Q,那么恭喜您,TDD有可能成为您显著提升工作效率的一件法宝。请不要信疑Q若卌,因ؓM一Ҏ的技术——只要是从根本上改变人的行ؓ方式的技术——就必然使得怿它的来越怿Q不信的来越不信。这好比学游泳Q唯一能学会游泳的途径是亲自下去游,除此之外别无他法。这也好比成功学Q即使把卡耐基或希博士的书倒背如流也不能拥有积极的心态,可当你以U极的心态去成就了一番事业之后,你就再也M开它了。相信我QTDD也是q样Q想试用TDD的h们,请遵循下面的步骤Q?/p>

~写TestCase --> 实现TestCase --> 重构
Q确定范围和目标Q?/td>   Q增加功能)   Q提升设计)

[友情提示Q敏捷徏模中的一个相当重要的实践被称为:Prove it With CodeQ这U想法和TDD不谋而合。]

{TDD的优点}

    『充满吸引力的优炏V?/b>

  1. 完工时完工。表明我可以很清楚的看到自己的这D工作已l结束了Q而传l的方式很难知道什么时候编码工作结束了?
  2. 全面正确的认识代码和利用代码Q而传l的方式没有q个Z?
  3. 为利用你成果的h提供SampleQ无论它是要利用你的源代码,q是直接重用你提供的lg?
  4. 开发小l间降低了交成本,提高了相互信赖程度?
  5. 避免了过渡设计?
  6. pȝ可以与详的试集一起发布,从而对E序的将来版本的修改和扩展提供方ѝ?
  7. TDDl了我们自信Q让我们今天的问题今天解冻I明天的问题明天解冻I今天不能解决明天的问题,因ؓ明天的问题还没有出现(没有TestCase)Q除非有TestCase否则我决不写M代码Q明天也不必担心今天的问题,只要我亮了绿灯?

    『不显而易见的优点?/b>

  8. 逃避了设计角艌Ӏ对于一个敏L开发小l,每个人都在做设计?
  9. 大部分时间代码处在高质量状态,100Q的旉里成果是可见的?
  10. ׃可以保证~写试和编写代码的是相同的E序员,降低了理解代码所p的成本?
  11. 为减文档和代码之间存在的细微的差别和由q种差别所引入的Bug作出杰出贡献?
  12. 在预先设计和紧急设计之间徏立一U^衡点Qؓ你区分哪些设计该事先做、哪些设计该q代时做提供了一个可靠的判断依据?

    『有争议的优炏V?/b>

  13. 事实上提高了开发效率。每一个正在用TDDq相信TDD的h都会怿q一点,但观望者则不同Q不怿TDD的h甚至坚决反对q一点,q很正常Q世界Lq样?
  14. 发现比传l测试方式更多的Bug?
  15. 使IDE的调试功能失L义,或者应该说Q避免了令h头痛的调试和节约了调试的旉?
  16. L处在要么~程要么重构的状态下Q不会人抓狂。(两顶帽子Q?
  17. 单元试非常有趣?
{TDD的步骤}
~写TestCase --> 实现TestCase --> 重构
Q不可运行)   Q可q行Q?/td>   Q重构)
步骤 制品
Q?Q快速新增一个测试用?/td> 新的TestCase
Q?Q编译所有代码,刚刚写的那个试很可能编译不通过 原始的TODO List
Q?Q做可能少的改动,让编译通过 Interface
Q?Q运行所有的试Q发现最新的试不能~译通过 Q?Red Bar)
Q?Q做可能少的改动,让测试通过 Implementation
Q?Q运行所有的试Q保证每个都能通过 Q?Green Bar)
Q?Q重构代码,以消除重复设?/td> Clean Code That Works

{FAQ}

[什么时候重构?]
如果您在软g公司工作Q就意味着您成天都会和想通过重构改善代码质量的想法打交道Q不仅您如此Q您的大部分同事也都如此。可是,I竟什么时候该重构Q什么情况下应该重构呢?我相信您和您的同事可能有很多不同的看法,最常见的答案是“该重构旉构”,“写不下ȝ时候重构”,和“下一ơP代开始之前重构”,或者干脆就是“最q没旉Q就不重构了Q下ơ有旉的时候重构吧”。正如您已经预见到我惌的——这些想法都是对重构的误解。重构不是一U构Y件的工具Q不是一U设计Y件的模式Q也不是一个Y件开发过E中的环节,正确理解重构的h应该把重构看成一U书写代码的方式Q或习惯Q重构时时刻L可能发生。在TDD中,除去~写试用例和实现测试用例之外的所有工作都是重构,所以,没有重构M设计都不能实现。至于什么时候重构嘛Q还要分开看,有三句话是我的经验:实现试用例旉构代码,完成某个Ҏ时重构设计Q品的重构完成后还要记得重构一下测试用例哦?/p>

[什么时候设计?]
q个问题比前面一个要隑֛{的多,实话实说Q本人在依照TDD开发Y件的时候也常常被这个问题困扎ͼL觉得有些问题应该在写试用例之前定下来,而有些问题应该在新增一个一个测试用例的q程中自然出玎ͼ水到渠成。所以,我的是,设计的时机应该由开发者自己把握,不要受到TDD方式的限Ӟ但是Q不需要事先确定的事一定不能事先确定,免得捆住了自q手脚?/p>

[什么时候增加新的TestCaseQ]
没事做的时候。通常我们认ؓQ如果你要增加一个新的功能,那么先写一个不能通过?TestCaseQ如果你发现了一个bugQ那么先写一个不能通过的TestCaseQ如果你现在什么都没有Q从0开始,请先写一个不能通过?TestCase。所有的工作都是从一个TestCase开始。此外,q要注意的是Q一些大师要求我们每ơ只允许有一个TestCase亮红灯,在这?TestCase没有Green之前不可以写别的TestCaseQ这U要求可以适当考虑Q但即有多个TestCase亮红灯也不要紧,q未q反TDD 的主要精?/p>

[TestCase该怎么写?]
试用例的编写实际上是两个q程Q用尚不存在的代码和定义这些代码的执行l果。所以一?TestCase也就应该包括两个部分——场景和断言。第一ơ写TestCase的h会有很大的不适应的感觉,因ؓ你之前所写的所有东襉K是在解决问题Q现在要你提出问题确实不大习惯,不过不用担心Q你正在做正的事情Q而这个世界上最隄事情也不在于如何解决问题Q而在于ask the right questionQ?/p>

[TDD能帮助我消除Bug吗?]
{:不能Q千万不要把“测试”和“除虫”؜Z谈!“除虫”是指程序员通过自己的努力来减少bug的数量(消除bugq样的字眼我们还是不要讲为好^_^Q,而“测试”是指程序员书写产品以外的一D代码来保产品能有效工作。虽然TDD所~写的测试用例在一定程度上为寻找bug提供了依据,但事实上Q按照TDD的方式进行的软g开发是不可能通过TDD再找到bug的(x我们前面说的“完工时完工”)Q你惛_Q当我们的代码完成的时候,所有的试用例都亮了绿灯,q时隐藏在代码中的bug一个都不会露出马脚来?/p>

但是Q如果要问“测试”和“除虫”之间有什么联p,我相信还是有很多话可以讲的,比如TDD事实上减了bug的数量,把查找bug战役的关注点从全U战场提升到代码战场以上。还有,bug的最可怕之处不在于隐藏之深Q而在于满天遍野。如果你发现了一个用户很不容易才能发现的bugQ那么不一定对工作做出了什么杰A献,但是如果你发CD代码中Qbug的密度或LE度q高Q那么恭喜你Q你应该抛弃q写这D代码了。TDD避免了这U情况,所以将Lbug的工作降低到了一个新的低度?/p>

[我该Z个Feature~写TestCaseq是Z个类~写TestCaseQ]
初学者常问的问题。虽然我们从TDD 的说明书上看到应该ؓ一个特性编写相应的TestCaseQ但Z么著名的TDD大师所写的TestCase都是和类/Ҏ一一对应的呢Qؓ了解释这个问题,我和我的同事们都做了很多试验Q最后我们得C一个结论,虽然我不知道是否正确Q但是如果您没有{案Q可以姑且相信我们?/p>

我们的研I结果表明,通常在一个特性的开发开始时Q我们针对特性编写测试用例,如果您发现这个特性无法用TestCase表达Q那么请这个特性细分,直至您可以ؓ手上的特性写出TestCase为止。从q里开始是最安全的,它不会导致Q何设计上重大的失误。但是,随着您不断的重构代码Q不断的重构 TestCaseQ不断的依据TDD的思想做下去,最后当产品伴随试用例集一起发布的时候,您就会不l意的发现经q重构以后的试用例很可能是和品中的类/Ҏ一一对应的?/p>

[什么时候应该将全部试都运行一遍?]
Good QuestionQ大师们要求我们每次重构之后都要完整的运行一遍测试用例。这个要求可以理解,因ؓ重构很可能会改变整个代码的结构或设计Q从而导致不可预见的后果Q但是如果我正在开发的是一个ERP怎么办?q行一遍完整的试用例可能花Ҏ个小Ӟ况且现在很多重构都是由工具做到的Q这个要求的可行性和前提条g都有所动摇。所以我认ؓ原则上你可以挑几个你觉得可能受到本次重构影响的TestCase去runQ但是如果运行整个测试包只要p数秒的时_那么不介意你按大师的要求d?/p>

[什么时候改q一个TestCaseQ]
增加的测试用例或重构以后的代码导致了原来的TestCase的失M效果Q变得无意义Q甚臛_能导致错误的l果Q这时是改进TestCase的最好时机。但是有时你会发玎ͼq样做仅仅导致了原来的TestCase在设计上是臃肿的Q或者是冗余的,q都不要紧,只要它没有失效,你仍然不用去改进它。记住,TestCase不是你的产品Q它不要好看Q也不要怎么太科学,甚至没有性能要求Q它只要能完成它的命就可以了——这也证明了我们后面所说的“用Ctrl-C/Ctrl-V~写试用例”的可行性?/p>

但是Q美国h的想法其实跟我们q是不太一P拿托巴赞的MindMap来说吧,其实画MindMap只是Z表现自己的思\Q或记忆某些重要的事情,但托却大家把MindMapL一件艺术品Q甚臌有很多艺术家把自q的抽象派MindMap拿出来帮助托做宣传。同P大师们也要求我们把TestCase写的跟代码一栯量精良,可我惌的是Q现在国内有几个公司能把产品的代码写的精良?Q还是一步一步慢慢来吧?/p>

[Z么原来通过的测试用例现在不能通过了?]
q是一个警报,Red AlertQ它可能表达了两层意思——都不是什么好意思—?Q你刚刚q行的重构可能失败了Q或存在一些错误未被发玎ͼ臛_重构的结果和原来的代码不{h了?Q你刚刚增加的TestCase所表达的意思跟前面已经有的TestCase相冲H,也就是说Q新增的功能q背了已有的设计Q这U情况大部分可能是之前的设计错了。但无论哪错了,无论是那层意思,x到这个问题的Ҏ都比TDD的正常工作要难?/p>

[我怎么知道那里该有一个方法还是该有一个类Q]
q个问题也是常常出现在我的脑中Q无Z是第一ơ接触TDD或者已l成?TDD专家Q这个问题都会缠l着你不放。不q问题的{案可以参考前面的“什么时候设计”一节,{案不是唯一的。其实多数时候你不必考虑未来Q今天只做今天的事,只要有重构工P从方法到cd从类到方法都很容易?/p>

[我要写一个TestCaseQ可是不知道从哪里开始?]
从最重要的事开始,what matters mostQ从脚下开始,从手头上的工作开始,从眼前的事开始。从一个没有UI的核心特性开始,从算法开始,或者从最有可能耽误旉的模块开始,从一个最严重的bug开始。这是TDDM者和鼠目寸光者的一个共同点Q不同点是前者早已成竹在胸?/p>

[Z么我的测试L看v来有Ҏ蠢?]
哦?是吗Q来Q握个手Q我的也是!不必担心q一点,事实上,大师们给的例子也相当愚蠢Q比如一个极端的例子是要写一个两个int变量相加的方法,大师先断a2+3=5Q再断言5+5=10Q难道这些代码不是很愚蠢吗?其实q只是一个极端的例子Q当你初ơ接触TDDӞ写这L代码没什么不好,以后当你熟练时就会发现这样写没必要了Q要CQ谦虚是通往TDD的必l之路!从经典开发方法{向TDD像从面向过E{向面向对象一样困难,你可能什么都懂,但你写出来的cL有一个纯OO的!我的同事q告诉我真正的太极拳Q其速度是很快的Q不比Q何一个快拌慢,但是初学者(通常是指学习太极拳的?0q_太不Ҏ把每个姿劉K做对Q所以只能慢慢来?/p>

[什么场合不适用TDDQ]
问的好,实有很多场合不适合使用TDD。比如对软g质量要求极高的军事或U研产品——神州六P人命兛_的Y件——医疗设备,{等Q再比如设计很重要必L前做好的软gQ这些都不适合TDDQ但是不适合TDD不代表不能写TestCaseQ只是作用不同,C不同|了?/p>

{Best Practise}

[微笑面对~译错误]
学生时代最x的是~译错误Q编译错误可能会被老师视ؓ上课不认真听评证据Q或者同学间怺嘲笑的砝码。甚至离开学校很多q的老程序员依然x它像x迟CP潜意识里g~译错误极有可能和工资挂钩(或者和智商挂钩Q反正都不是什么好事)。其实,只要提交到版本管理的代码没有~译错误可以了Q不要担心自己手上的代码的编译错误,通常Q编译错误都集中在下面三个方面:
Q?Q你的代码存在低U错?br />Q?Q由于某些Interface的实现尚不存在,所以被试代码无法~译
Q?Q由于某些代码尚不存在,所以测试代码无法编?br />h意第二点与第三点完全不同Q前者表明设计已存在Q而实C存在D的编译错误;后者则指仅有TestCase而其它什么都没有的情况,设计和实现都不存在,没有Interface也没有Implementation?/p>

另外Q编译器q有一个优点,那就是以最敏捷的n手告诉你Q你的代码中有那些错误。当然如果你拥有Eclipseq样可以及时提示~译错误的IDEQ就不需要这L功能了?/p>

[重视你的计划清单]
在非TDD的情况下Q尤其是传统的瀑布模型的情况下Q程序员不会不知道该做什么,事实上,L有设计或者别的什么制品在引导E序员开发。但是在TDD的情况下Q这U优势没有了Q所以一个计划清单对你来说十分重要,因ؓ你必自己发现该做什么。不同性格的h对于q一点会有不同的反应Q我怿qx做事没什么计划要依靠别h安排的hQ所谓将才)可能略有不适应Q不q不要紧QTasks和CalendarQ又U效率手册)早已成ؓC上班族的必备工具了;而^时工作生zd很有计划性的人,比如?)Q就会更喜欢q种自己可以掌控Plan的方式了?/p>

[废黜每日代码质量查]
如果我没有记错的话,PSP对于个h代码查的要求是蛮严格的,而同h在针对个人的问题上, TDD却徏议你废黜每日代码质量查,别v疑心Q因ZL在做TestCase要求你做的事情,q且L有办法(自动的)查代码有没有做到q些事情 ——红灯停l灯行,所以每日代码检查的旉可能被节省,对于一个严格的PSP实践者来_q个成本q是很可观的Q?/p>

此外Q对于每日代码质量检查的另一个好处,是帮助你认识自q代码Q全面的从宏观、微观、各个角度审视自q成果Q现在,当你依照TDD做事Ӟq个优点也不需要了Q还记得前面说的TDD的第二个优点吗,因ؓ你已l全面的使用了一遍你的代码,q完全可以达到目的?/p>

但是Q问题往往也ƈ不那么简单,现在有没有h能告诉我Q我如何全面审视我所写的试用例呢?别忘了,它们也是以代码的形式存在的哦。呵呵,但愿q个问题没有把你吓到Q因为我怿到目前ؓ止,它还不是瓉问题Q况且在~写产品代码的时候你q是会自ȝ发现很多试代码上的没考虑到的地方Q可以就此修改一下。道理就是如此,世界上没有Q何方法能代替你思考的q程Q所以也没有MҎ能阻止你犯错误,TDD仅能让你更容易发现这些错误而已?/p>

[如果无法完成一个大的测试,׃最的开始]
如果我无法开始怎么办,教科书上有个很好的例子:我要写一个电影列表的c,我不知道如何下手Q如何写试用例Q不要紧Q首先想象静态的l果Q如果我的电影列表刚刚徏立呢Q那么它应该是空的,OKQ就写这个断a吧,断言一个刚刚初始化的电影列表是I的。这不是愚蠢Q这是细节,奥运会五全能的金牌得主玛丽莲·金是这栯的:“成功h士的共同点在于……如果目标不够清晎ͼ他们会首先做通往成功道\上的每一个细步骤……”?/p>

[试~写自己的xUnit]
Kent Beck大家每当接触一个新的语a或开发^台的时候,p己写q个语言或^台的xUnitQ其实几乎所有常用的语言和^台都已经有了自己?xUnitQ而且都是大同异Q但是ؓ什么大师给Zq样的徏议呢。其实Kent Beck的意思是说通过q样的方式你可以很快的了解这个语a或^台的Ҏ,而且xUnit实很简单,只要知道原理很快p写出来。这对于那些喜欢自己写底层代码的人,或者喜Ƣ控制力的h而言是个好消息?/p>

[善于使用Ctrl-C/Ctrl-V来编写TestCase]
不必担心TestCase会有代码冗余的问题,让它冗余好了?/p>

[永远都是功能FirstQ改q可以稍后进行]
上面q个标题q可以改成另外一句话Q避免过渡设计!

[淘汰陈旧的用例]
舍不得孩子套不着狹{不要可惜陈旧的用例Q因为它们可能从概念上已l是错误的了Q或仅仅会得出错误的l果Q或者在某次重构之后失去了意义。当然也不一定非要删除它们,从TestSuite中除去(JUnitQ或加上IgnoredQNUnitQ标{也是一个好办法?/p>

[用TestCase做试验]
如果你在开始某个特性或产品的开发之前对某个领域不太熟悉或一无所知,或者对自己在该领域里的能力一无所知,那么你一定会选择做试验,在有单元试作工L情况下,你用TestCase做试验,q看h像你在写一个验证功能是否实现的 TestCase一P而事实上也一P只不q你所验证的不是代码本w,而是q些代码所依赖的环境?/p>

[TestCase之间应该量独立]
保证单独q行一个TestCase是有意义的?/p>

[不仅试必须要通过的代码,q要试必须不能通过的代码]
q是一个小技巧,也是不同于设计思\的东ѝ像界的值或者ؕ码,或者类型不W的变量Q这些输入都可能会导致某个异常的抛出Q或者导致一个标C“illegal parameters”的q回|q两U情况你都应该测试。当然我们无法枚举所有错误的输入或外部环境,q就像我们无法枚举所有正的输入和外部环境一P只要TestCase能说明问题就可以了?/p>

[~写代码的第一步,是在TestCase中用Ctrl-C]
q是一个高U技巧,呃,是的Q我是这个意思,我不是说q个技巧难以掌握,而是说这个技巧当且仅当你已经是一个TDD高手Ӟ你才能体会到它的力。多ơ用TDD的h都有q样的体会,既然我的TestCase已经写的很好了,很能说明问题Qؓ什么我的代码不能从TestCase拯一些东西来呢。当Ӟq要求你的TestCase已经h很好的表达能力,比如断言f (5)=125的方式显然没有断af(5)=5^(5-2)表达更多的内宏V?/p>

[试用例包应该尽量设计成可以自动q行的]
如果产品是需要交付源代码的,那我们应该允许用户对代码q行修改或扩充后在自q环境下run整个试用例包。既焉常情况下的产品是可以自动运行的Q那Z么同样作Z付用L制品Q测试用例包׃是自动运行的呢?即产品不需要交付源代码Q测试用例包也应该设计成可以自动q行的,qؓ试部门或下一版本的开发h员提供了极大的便利?/p>

[只亮一盏红灯]
大师的徏议,前面已经提到了,仅仅是徏议?/p>

[用TestCase描述你发现的bug]
如果你在另一个部门的同事使用了你的代码,q且Q他发现了一个bugQ你猜他会怎么做?他会立即走到你的工位边上Q大声斥责说Q“你有bugQ”吗Q如果他胆敢q样对你Q对不vQ你一定要冷静下来Q不要当面回骂他Q相反你可以微微一W,然后心^气和的对他说Q“哦Q是吗?那么好吧Q给我一个TestCase证明一下。”现在局势已l倒向你这一边了Q如果他q没有准备好回答你这致命的一击,我猜他会感到非常愧Qƈ在内心责怪自己太莽撞。事实上Q如果他的TestCase没有q多的要求你的代码(而是按你们事前的契约Q,q且亮了U灯Q那么就可以定是你的bugQ反之,Ҏ则无理了。用TestCase描述bug的另一个好处是Q不会因Z后的修改而再ơ暴露这个bugQ它已经成ؓ你发布每一个版本之前所必须查的内容了?/p>

{关于单元试}

单元试的目标是

Keep the bar green to keep the code clean

q句话的含义是,事实上我们只做两件事情:让代码奏效(Keep the bar greenQ和让代码洁净QKeep the code cleanQ,前者是把事情做对,后者是把事情做好,两者既是TDD中的两顶帽子Q又是xUnit架构中的因果关系?/p>

单元试作ؓ软g试的一个类别,q是xUnit架构创造的Q而是很早有了。但是xUnit架构使得单元试变得直接、简单、高效和规范Q这也是单元试最q几q飞速发展成量一个开发工具和环境的主要指标之一的原因。正如Martin Fowler所_“Y件工E有史以来从没有如此众多的h大大收益于如此简单的代码Q”而且多数语言和^台的xUnit架构都是大同异Q有的仅是语a不同Q其中最有代表性的是JUnit和NUnitQ后者是前者的创新和扩展。一个单元测试框架xUnit应该Q?Q每个TestCase独立q行Q?Q每个TestCase可以独立和报告错误Q?Q易于在每次q行之前选择TestCase。下面是我枚丑և的xUnit框架的概念,q些概念构成了当前业界单元测试理论和工具的核心:

[试Ҏ/TestMethod]
试的最单位,直接表示Z码?/p>

[试用例/TestCase]
由多个测试方法组成,是一个完整的对象Q是很多TestRunner执行的最单位?/p>

[试容器/TestSuite]
由多个测试用例构成,意在把相同含义的试用例手动安排在一PTestSuite可以呈树状结构因而便于管理。在实现ӞTestSuite形式上往往也是一个TestCase或TestFixture?/p>

[断言/Assertion]
断言一般有三类Q分别是比较断言Q如assertEqualsQ,条g断言Q如isTrueQ,和断a工具Q如failQ?/p>

[试讑֤/TestFixture]
为每个测试用例安排一个SetUpҎ和一个TearDownҎQ前者用于在执行该测试用例或该用例中的每个测试方法前调用以初始化某些内容Q后者在执行该测试用例或该用例中的每个方法之后调用,通常用来消除试对系l所做的修改?/p>

[期望异常/Expected Exception]
期望该测试方法抛出某U指定的异常Q作Z个“断a”内容,同时也防止因为合情合理的异常而意外的l止了测试过E?/p>

[U类/Category]
为测试用例分c,实际使用时一般有TestSuite׃再用CategoryQ有Category׃再用TestSuite?/p>

[忽略/Ignored]
讑֮该测试用例或试Ҏ被忽略,也就是不执行的意思。有些被抛弃的TestCase不愿删除Q可以定为Ignored?/p>

[试执行?TestRunner]
执行试的工P表示以何U方式执行测试,别误会,q可不是在代码中规定的,完全是与试内容无关的行为。比如文本方式,AWT方式Qswing方式Q或者Eclipse的一个视囄{?/p>

{实例QFibonacci数列}

下面的Sample展示TDDer是如何编写一个旨在生Fibonacci数列的方法?br />Q?Q首先写一个TCQ断afib(1) = 1;fib(2) = 1;q表C数列的第一个元素和W二个元素都??/p>

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
}

Q?Q上面这D代码不能编译通过QGreatQ——是的,我是说GreatQ当Ӟ如果你正在用的是Eclipse那你不需要编译,Eclipse 会告诉你不存在fibҎQ单击mark会问你要不要新徏一个fibҎQOhQ当ӞZ让上面那个TC能通过Q我们这样写Q?/p>

public   int  fib(  int  n ) {
        
return   1 ;
}

Q?Q现在那个TC亮了l灯QwowQ应该庆一下了。接下来要增加TC的难度了Q测W三个元素?/p>

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(
2 , fib( 3 ));
}

不过q样写还不太好看Q不如这样写Q?/p>

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(fib(
1 ) + fib( 2 ), fib( 3 ));
}

Q?Q新增加的断aD了红灯,Z扭{q一局势我们这样修改fibҎQ其中部分代码是从上面的代码中Ctrl-C/Ctrl-V来的Q?/p>

public   int  fib(  int  n ) {
        
if  ( n  ==   3  )  return  fib( 1 ) + fib( 2 );
        
return   1 ;
}

Q?Q天哪,q真是个׃h写的代码Q是啊,不是吗?因ؓTC是产品的蓝本,产品只要恰好满TCok。所以事情发展到q个地步不是fibҎ的错Q而是TC的错Q于是TCq要q一步要求:

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(fib(
1 ) + fib( 2 ), fib( 3 ));
        assertEquals(fib(
2 ) + fib( 3 ), fib( 4 ));
}

Q?Q上有政{下有对{?/p>

public   int  fib(  int  n ) {
        
if  ( n  ==   3  )  return  fib( 1 ) + fib( 2 );
        
if  ( n  ==   4  )  return  fib( 2 ) + fib( 3 );
        
return   1 ;
}

Q?Q好了,不玩了。现在已l不是贱不贱的问题了Q现在的问题是代码出C冗余Q所以我们要做的是——重构:

public   int  fib(  int  n ) {
        
if  ( n  ==   1   ||  n  ==   2  Q?/span> return   1 ;
        
else   return  fib( n  -   1  )  +  fib( n  -   2  );
}

Q?Q好Q现在你已经fibҎ已经写完了吗Q错了,一个危险的错误Q你忘了错误的输入了。我们o0表示Fibonacci中没有这一V?/p>

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(fib(
1 ) + fib( 2 ), fib( 3 ));
        assertEquals(fib(
2 ) + fib( 3 ), fib( 4 ));
        assertEquals(
0 , fib( 0 ));
        assertEquals(
0 , fib( - 1 ));
}

then change the method fib to make the bar greanQ?/p>

public   int  fib(  int  n ) {
        
if  ( n  <=   0  )  return   0 ;
        
if  ( n  ==   1   ||  n  ==   2  Q?/span> return   1 ;
        
else   return  fib( n  -   1  )  +  fib( n  -   2  );
}

Q?Q下班前最后一件事情,把TC也重构一下:

public   void  testFab() {
        
int  cases[][]  =  {
                {
0 0 }, { - 1 0 },   // the wrong parameters
                { 1 1 }, { 2 1 }};   // the first 2 elements

        
for  ( int  i  =   0 ; i  <  cases.length; i ++ )
                assertEquals( cases[i][
1 ], fib(cases[i][ 0 ]) );

        
// the rest elements
         for  ( int  i  =   3 ; i  <   20 ; i ++ )
                assertEquals(fib(i
- 1 ) + fib(i - 2 ), fib(i));
}

Q?0Q打完收工?/p>

{关于本文的写作}

在本文的写作q程中,作者也用到了TDD的思维Q事实上作者先构思要写一什么样的文章,然后写出q篇文章应该满的几个要求,包括功能的要求(要写些什么)和性能的要求(可读性如何)和质量的要求Q文字的要求Q,q些要求起初是一个也达不到的Q因为正文还一个字没有Q,在这U情况下作者的文章无法~译通过Qؓ了达到这些要求,作者不停的写啊写啊Q终于在花尽了两个月的心血之后完成了当初既定的所有要求(make the bar greenQ,随后作者整理了一下文章的l构Q重构)Q在满意的提交给了Blogpȝ之后Q作者穿上了一件绿色的汗衫Q趴在地上,学了两声青蛙叫。。。。。。。^_^

{后记QMartin Fowler在中国}

从本文正式完成到发表的几个小旉Q我偶然d了Martin Fowler先生北京访谈录,光提到了很多对试驱动开发的看法Q摘抄在此:

Martin FowlerQ当Ӟ值得׃半的旉来写单元试Q!因ؓ单元试能够使你更快的完成工作。无数次的实践已l证明这一炏V你的时间越是紧张,p要写单元试Q它看上LQ但实际上能够帮助你更快、更舒服地达到目的?br />Martin FowlerQ什么叫重要Q什么叫不重要?q是需要逐渐认识的,不是惛_然的。我为绝大多数的模块写单元测试,是有点烦人,但是当你意识到这工作的h值时Q你会欣然的?br />Martin FowlerQ对全世界的E序员我都是那么几条Q……第二,学习试驱动开发,q种新的Ҏ会改变你对于软g开发的看法。…?/font>

——《程序员》,2005q?月刊

{鸣谢}

fhawk
Dennis Chen
般若菩提
Kent Beck
Martin Fowler
c2.com



Stone Jiang 2006-04-28 13:03 发表评论
]]>
李开复《给中国学生的第五封信?http://www.shnenglu.com/ace/archive/2006/04/28/6414.htmlStone JiangStone JiangFri, 28 Apr 2006 04:43:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/28/6414.htmlhttp://www.shnenglu.com/ace/comments/6414.htmlhttp://www.shnenglu.com/ace/archive/2006/04/28/6414.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6414.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6414.html 李开复《给中国学生的第五封信?/a>

阅读W记Q积极主动,充满热情Q是q封信的核心。最q自׃l历了一些事情,在面临未来发展前景的分岔道\Q需要做出选择Ӟ明知道某一条具有更大的发展前景Q但是却׃未知的待遇问题放弃了选择它,q不积极主动,不充满热情,但是某些时候有很多的外在因素可以媄响决定?/strong>

李开复《给中国学生的第五封信》,l箋传授关于U极行动、把握自己命q的人生l验Q这是“一有关积极主动的信”?

    “今天大多数优秀的企业对人才的期望是Q积极主动、充满热情、灵z自信的人”,学会“自q事,自己负责Q自p决”,“我不能Q别的Q何h也不能代替你走过那条路;你必自己去走。?/p>

一个h被击败,不是因ؓ外界环境的阻,而是取决于他对环境如何反应。“有勇气改变可以改变的事情,有胸怀接受不可改变的事情,有智慧来分L两者的不同。?/p>

    “如果你想知道什么,p己到|上LQ不要急着去问别hQ如果你听到了什么,不要盲目信从Q应当自׃动去|上求证。?/p>

    被动是弃权Q不做决定也是一U决定。下定决心,每一件小事都要表辑և自己的意见,q你不是很在乎。例如,自己军_在餐馆点什么菜Q自己决定自q衣着打扮Q周末时自己军_要去哪里玩,{等。设法让自己潜意识里的“我感觉Q我惌”体现出来,不要被动Q不要从众,避免盲目听从父母、老师、名人…?/p>

    U极d的hL在言语中赋予自己军_的权利,他们喜欢说的话包括:“试试看有没有其他的可能性。”“也许我可以换个思\。”“我可以控制自己的情l。”“我可以惛_更有效的表达方式。”“我的感觉是……”“我选择……”“我要……”“我情愿……”“我打算……”“我军_……”等{。积极主动地抓住命运中你可以选择、可以改变、可以最大化你的影响力的部分?/p>

    国人很喜欢试不同的工作,他们一生中q_要换四次工作。经常给自己讄一些极h战性、但l非遥不可及的目标?/p>

    “等待的Ҏ有两U,一U是什么事也不做地I等Q另一U是一边等Q一Ҏ事情向前推动。”在机遇q没有来临时Q就应事事用心,事事力。一旦机遇到来,一定要全力以ʎQ把握机遇?/p>

    在公叔RQ经常得到晋升机会的人,大多是能够积极推销和表达自q、有q取心的人。当他们q是公司的一名普通员工时Q只要和公司利益或者团队利益相关的事情Q他们就会不遗余力地发表自己的见解、A献自qdQ帮助公司制定和安排工作计划Q在完成本职工作后,他们总能协助其他人尽快完成工作;他们常常鼓励自己和同_提高整个队伍的士气;q些人L以事为本、以事ؓ先——他们都是最U极d的h?/p>

    在h生的旅途中Q你是你自己惟一的司机,千万不要让别人驾驶你的生命之车。你要稳E_坐在司机的位|上Q决定自׃时要停、要倒R、要转弯、要加速、要刹R{等。只有积极主动的人才能在瞬息万变的竞争环境中赢得成功Q只有善于展Cq人才能在工作中获得真正的Z?/p>

    “你们的旉有限Q所以不要浪Ҏ间在别h的生z里。不要被信条所??盲从信条是活在别人的生活里。不要让M人的意见Ҏ了你内在的心声。重要的Q拥有跟随内心和直觉的勇气。你的内心与直觉知道你真正想成ؓ什么样的h。Q何其他事物都是次要的。”——斯蒂夫 乔布?Q苹果公司总裁Q于2005q斯坦福大学毕业典礼?/p>

Stone Jiang 2006-04-28 12:43 发表评论
]]>
我对重构的理?(?http://www.shnenglu.com/ace/archive/2006/04/28/6412.htmlStone JiangStone JiangFri, 28 Apr 2006 04:34:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/28/6412.htmlhttp://www.shnenglu.com/ace/comments/6412.htmlhttp://www.shnenglu.com/ace/archive/2006/04/28/6412.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6412.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6412.htmlFrom: http://www.cnblogs.com/lane_cn/archive/2006/02/05/325782.html

什么是重构

重构Q用最单的一句话_是要在不改变系l功能的情况下,对系l的内部l构q行重新调整。重构的最直接目的在于改进软gpȝ的内部架构。一个好的结构可以更加适应于需求的变化Q更好的满客户的需求,最大限度的廉软gpȝ的生命周期?br />
Z么要重构

在不改变pȝ功能的情况下Q改变系l的实现方式。ؓ什么要q么做?投入_֊不用来满_户关心的需求,而是仅仅改变了Y件的实现方式Q这是否是在费客户的投资呢Q?br />
重构的重要性要从Y件的生命周期说v。Y件不同与普通的产品Q他是一U智力品,没有具体的物理Ş态。一个Y件不可能发生物理损耗,界面上的按钮永远不会因ؓ按动ơ数太多而发生接触不良。那么ؓ什么一个Y件制造出来以后,却不能永q用下dQ?br />
对Y件的生命造成威胁的因素只有一个:需求的变更。一个Y件LxU特定的需求而生,时代在发展,客户的业务也在发生变化。有的需求相对稳定一些,有的需求变化的比较剧烈Q还有的需求已l消׃Q或者{化成了别的需求。在q种情况下,软g必须相应的改变?br />
考虑到成本和旉{因素,当然不是所有的需求变化都要在软gpȝ中实现。但是ȝ说来QY件要适应需求的变化Q以保持自己的生命力?br />
q就产生了一U糟p的现象QY件品最初制造出来,是经q精心的设计Q具有良好架构的。但是随着旉的发展、需求的变化Q必M断的修改原有的功能、追加新的功能,q免不了有一些缺陷需要修攏Vؓ了实现变_不可避免的要q反最初的设计构架。经q一D|间以后,软g的架构就千疮癑֭了。bug来多Q越来越隄护,新的需求越来越隑֮玎ͼ软g的构架对新的需求渐渐的失去支持能力Q而是成ؓ一U制U。最后新需求的开发成本会过开发一个新的Y件的成本Q这是q个软gpȝ的生命走到尽头的时候?br />
重构p够最大限度的避免q样一U现象。系l发展到一定阶D后Q用重构的方式Q不改变pȝ的外部功能,只对内部的结构进行重新的整理。通过重构Q不断的调整pȝ的结构,使系l对于需求的变更始终h较强的适应能力?br />
拒绝变化 VS 拥抱变化

按照传统的Y件设计方式,软g的生产分为需求调查、概要设计、详l设计、编码、单体测试、联合测试、现场部|几个阶Dc虽说这几个阶段是可以互相渗透,但是ȝ来说是有一定次序的Q前一个阶D늚工作是后一个阶D工作的基础。这向下面q样一UV形的模式Q?br />
vdevelope.GIF

往下的方向系l进行分解,往上的方向系l进行整合。这L开发Ş式将软g开发分计前和设计后两个阶段Q开发过E中存在一个重要的“里E碑”——设计说明书的。在设计说明书完成前Q工E处于“设计”阶D,而在设计说明书完成之后,工程则进入“实施”阶Dc一旦到了实施阶D,M需求或者设计上的变更都是非常困隄Q需要花费大量的成本。通常Z保证工程的顺利实施,开发h员常有这样一U冲动:按住客户的手Q在需求说明书上签字。ƈ且告诉客P“从今天开始,M需求变更都要停止,直到我们把现在这个东西做完。”这是一U拒l变化的开发方式?br />
软gpȝ要保持与企业的目标一致。时代在发展Qh们的要求在不断的提高Q客L业务在不断的发展。在q种情况下,传统的先设计、再施工的V形式已经不能适应日益复杂的业务需要。Y件工E逐渐演化成下面这Lq程Q?br />
sdevelope.GIF

说明一下:
1、Y件开发的目标要与企业目标保持一_一个开发周期不宜时间过长,一般控制在半年Cq。系l部|后Qƈ不意味着开发工作结束了Q而是q入了下一个周期?br />
2、工E以循环q代的方式前q,qƈ不意呌视了设计Q不是要搞边调研、边设计、边施工的“三边”工E,相反Q是更加重视设计的地位。Y件开发的全过E都需要设计,软g开发是“持l设计”的q程。同Ӟ设计工作也不只是单过E分解、Q务分配,而是概念设计、逻辑设计、物理设计等各个斚w互相交织、齐头ƈq?br />
传统的Y件开发方式用一U非常理惛_的流E——先与客戯论项目的范围Q确定哪些需要做Q哪些不需要做Q然后规划一个完的设计Q不仅可以满现在的需求,q能很好的适应未来的需求,设计完成后开始编码,然后试l装Q送到现场安装调试q行。这一pdq程qg发射一颗炮弹,首先要找到目标,然后Ҏ地Ş、风力、目标的位置、移动速度{各U因素,计算提前量、炮弹发的角度Q计出一个抛物线轨道Q最后在合适的旉把炮弹发出厅R这一切都W合最正确的物理定律,一切都听v来很理想。如果没有意外条Ӟ当然是可以击中目标的。但是炮弹一旦发出去,一切就失去了控ӞM环境的变化都会造成偏离目标。尤其是对于一个运动的目标来说Q计过E十分复杂,很多情况下只能靠Z计。对于不规则的运动目标只能碰运气。这L方式Q命中率是很低的?br />
新的软g开发过E不q求完美的、长期的、理想的计划Q更加重视实际情况,重视需求的变化Q提倡采用短期的计划。这是一U拥抱变化的q程。就象是在炮弹上安装了一个反馈装|,锁定目标后,保大方向的正确Q然后就炮弹发出厅R炮弹在q行q程中不断的目标位|偏U量输入反馈电\Q根据反馈输整自qq行路线Q无限的D目标。这P炮弹拥有了制导能力Q命中率大大增加?br />
重构可以增加工E的调整能力Q他可以把品回复到一个稳定的状态,可以Zq个状态达C一个目标。如此反复前q,更好的满_L需求?br />
保持兼容?br />
重构的目的在于改变系l的实现方式Q而不改变原有的功能。这个过E中Q判断兼Ҏ就十分的重要。一个子pȝ、模块、类、函数是否与升前保持兼容,如何判断q个兼容性,如何保持q个兼容性,q关pd重构的成本和重构的可能性?br />
E序员学习写E序代码Ӟ会发现随着E序代码愈来愈多Q许多的E序代码不断重复出现和被使用Q因此很自然的开始用例E、子E序或是q程、函数等机制帮助我们q行E序代码整理的工作。于是很自然的,字体的分析方式演化成q个样子Q将客户的需求过E进行分解,一步一步的分解Q直到可以直接的实现他。这是面向q程的分析方式?br />
面向q程的分析方式对变化的能力是很弱的。ؓ什么呢Q因为面向过E的分析方式很容易造成一U們֐——不区分行动的主体。一个过E是没有M的,他不是在己工作,而是在ؓ“别人”工作。当我们修改了一个过E之后,我们很难判断q个q程是否保持向后兼容Q其他过E会不会受到影响。因个过E对外界有意义的不仅是他的输入和输出Q还包括每一步过E,每一步过E都可能含有一个非帔R讳的业务意义Q对外界产生影响?br />
因此Q修改一个过E是非常困难的,通常升一个面向过E的pȝQ可以采用两U方式:
1、写新的q程Q?br />2、在原有的过E上加开兛_数?br />
除此以外的升U办法都很难保证原过E和新过E的兼容性,Ҏ造成错误?br />
Z更好的保证升U后模块的兼Ҏ,应该采用面向对象的分析方式。按照这L分析方式Q一个对象ؓ“自己”工作,他有完整的、独立的业务含义。对象之间通过接口发生联系Q一个对象对外界有媄响的部分只有接口Q至于他做什么、如何做、做的对不对Q则不是外界需要关心的事情?br />
判断一个接口升U后是否保持兼容性就是一件比较容易的事情了。我们可以判断接口的输入输出是否W合下面两条规则Q?br />1、升U后的输入是升前的输入的超U;
2、升U后的输出是升前的输出的子集?br />
只要W合q两点,他就仍然可以在系l中q行Q不会对其他对象造成危害。在实际的工E中Q判断这个兼Ҏ有一个更好的办法Q?strong>自动化的单元试?br />
在重构的q程中,自动化的单元试是非常好的保障。采用自动化的单元测试,不断q行试Q可以保证系l的l构改变的过E中Q业务行Z发生改变?img height="1" src="http://www.cnitblog.com/martin/aggbug/9810.html" width="1" />



Stone Jiang 2006-04-28 12:34 发表评论
]]>一“发飙”邮件的传播路径 (?http://www.shnenglu.com/ace/archive/2006/04/27/6359.htmlStone JiangStone JiangThu, 27 Apr 2006 01:12:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/27/6359.htmlhttp://www.shnenglu.com/ace/comments/6359.htmlhttp://www.shnenglu.com/ace/archive/2006/04/27/6359.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6359.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6359.html阅读全文

Stone Jiang 2006-04-27 09:12 发表评论
]]>
盘安装Fedora Core 4 全过E(WinXp+ FC4Q??http://www.shnenglu.com/ace/archive/2006/04/25/6248.htmlStone JiangStone JiangTue, 25 Apr 2006 08:27:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/25/6248.htmlhttp://www.shnenglu.com/ace/comments/6248.htmlhttp://www.shnenglu.com/ace/archive/2006/04/25/6248.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6248.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6248.html 盘安装Fedora Core 4 全过E(WinXp+ FC4Q?

 

1、整理出一个大?0GQ最?GQ左右的分区Q用PQ8分两个区Q一个Swap1.2GQ一个ext2/ext3的格式,用于安装Linuxpȝ

2、下载ISO文gC个FAT32格式的分ZQ约?.57GB大小Q如D:\FC4\

3、下载:Grub For Dos Q放到C盘(我的WinXP安装在C盘)根目录下

4、编辑C:\boot.ini 文gQ加入一?br />C:\grldr="GRUB For Dos/Windows "

5、编辑C:\boot\grub\menu.lst 加入Q?
title Install-Fedora Core 4
kernel (hd0,0)/isolinux/vmlinuz
initrd (hd0,0)/isolinux/initrd.img

5、重新启动电脑,依次选择Q?GRUB For Dos/Windows > Install-Fedora Core 4Q进入安装程序,在安装过E中当进行到选择安装介质Ӟ选硬盘安装,扑ֈ自己ISO攄的位|,如dev/sdm2/Fc4

6、进入正式安装,在选择分区的时候,把ext2/ext3的分行挂载即可,其他按照提示操作Q挂载简单的操作是选中ext2/ext3的分区,然后选择下拉菜单中的"/"卛_Q?/p>

7、安装,Ҏ模块的加载的多少Q机器的q行速度Q时间由20分钟?个小时不{?/p>

8、安装好之后讄用户名和密码Q注意FC4的用户名是区分大写?/p>

9、安装好之后?用户名root+密码 登陆

10、打开应用E序下的TerminalQ运行如下命令,对系l进行升U?br />#rpm -import /usr/share/rhn/RPM-GPG-KEY-fedora
#yum update

大约10分钟左右Q会列出800多兆需要更新的E序包,q行更新卛_

11、美化一下中文字?br />下蝲字体Q?a >http://www.bitman.cn/Linux/bkai00mp.ttf
q入文g夹: #cd /usr/share/fonts/chinese/TrueTypa
删除下面所有的字体Q然后把bkai00mp.ttf攄到如上目录即?/p>



Stone Jiang 2006-04-25 16:27 发表评论
]]>
输了q四q你p了一? (?http://www.shnenglu.com/ace/archive/2006/04/25/6238.htmlStone JiangStone JiangTue, 25 Apr 2006 05:00:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/25/6238.htmlhttp://www.shnenglu.com/ace/comments/6238.htmlhttp://www.shnenglu.com/ace/archive/2006/04/25/6238.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6238.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6238.html  输了q四q你p了一?/a>

于学计算机或者从事这斚w工作的朋?
1、首先,不论你是不是计算Z业的朋友Q相信一句话。“想成功不努力是不行的”,只要你努力,无论你是不是学计机的都会获得成功的。如果你不努力,无论你是不是学计机的,都很难؜的?br />2、无论学什么专业。一定要先做人,再做事,如果你做人有问题Q无Z做什么都是失败的。要提高自己的素质,需要^时有意识的积累。多交比较上q的朋友。别滥交朋友,比如多结交小w之类的朋?)
3、如果你以后的工作意x软g公司Q那么你必须要懂得至一门开发语a。即使你的志向比较高你也必须从开发开始,否则同事是瞧不v你的。而且较ؓ熟练的用,如果有相关的目l验最为合适。程序开发是很辛苦的工作,在你q没准备好这个方向之?一定要惛_q个是不是你惌?对于itq行?你必d备好随时学习新的东西,打算学一两个工具可以؜一生的x千万别想.如果你是本科?包括以下),q是你必要准备?
偶曾l用q的工具(旉序):aspQ比较熟l)QpbQ用q?个月,现在全忘讎ͼ,delphiQ用q一D|?现在忘的差不多),exchangeQ用q?个月,现在全忘讎ͼ,Lotus notes(用过2个月Q开发过一个论坛,也忘记的差不?QjavaQ主要是写jsp用。很用来写javaQ,primetonQ构件开发工P一直在用,比较熟练Q,plsqlQoracle客户端工Pl常用,比较熟练Q?br />逐步告别E序开发生zR?br />4、如果你是研I生Q包括比较有l验的从业h员)Q你的目标不应该是程序开发h员,臛_是设计h员。这个时候你有两个方向,一个是目理Q如目l理,另外一个是技术方向,如架构师.的公司可能一人兼多个职位.无论哪个方向都需要大量的实践l验.如果在毕业之前就有项目经验是非常有理?而善于ȝ的h那就能提升自q层次.E序开发h员不应该做ؓ职业的发展目?而是L.
5、在学校里千万不要浪Ҏ?要合理安排学校生z,让学校生zM富而收P“输了这四年你就输了一生”。要提高自己的学习能?要从思想上理?工具是次要的.无论什么语a,到工作的 时候都不够用的.所?我觉得在学校学习的时候要求面q?而不是精?当然,如果你对某一U语a有兴的?_N也无妨,我说的不要精?不是说随便学?我的意思是q个工具应付工程没有问题那就可以?是说可以独立开发一个模?而不要对工程中极用到的一些知识花大力气研I?我说q话?Z学校的时间是不够?其他q要很多需要学习的知识.
我觉得大学里必须要学会的东东:
学会做h(沟通能?成熟,l织能力,理解能力):可以多参加各U社团活?如果没有C֛zd,老乡zd也可?最好自ql?
p(臛_4U?:下点功夫.会有回报?q么说吧,对于四六U证??个证?相当于一个月?000rmb,q么说是不是有吸引力一?
专业评:软g工程,数据l构,数据库知?|络知识
我觉得这四门最为重?q不是说其他的不重要?其他的课上课不缺?考试中等以上可以了.q四门一定要学好.
6、我觉得现在市场的主是j2ee?net两大方向,无论哪个方向,都很有市?要精通的话都不是件容易的事情:)
你可以选择一个方?千万不要问别人选择什么好?q个问题p问篮球好q是毛球好一样难回答,如果你习惯了微Y的品那么就选择.net,如果你喜Ƣ自?不喜Ƣ垄?那么你选择j2ee.多利用网l资?要玩转网l资?不要被网l资源给玩了:)

 

赯个名字做标题Q是Z让还在读书的朋友要珍惜眼前的旉。其实h生的道\很长Q以前欠的Z后也可以q的。我的意思是q还不如早还Q早q不如不Ơ。不Ơ不如赚?/p>

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=675563



Stone Jiang 2006-04-25 13:00 发表评论
]]>
STL中map与hash_map容器的选择 (?http://www.shnenglu.com/ace/archive/2006/04/25/6223.htmlStone JiangStone JiangTue, 25 Apr 2006 01:25:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/25/6223.htmlhttp://www.shnenglu.com/ace/comments/6223.htmlhttp://www.shnenglu.com/ace/archive/2006/04/25/6223.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/6223.htmlhttp://www.shnenglu.com/ace/services/trackbacks/6223.html STL中map与hash_map容器的选择


Stone Jiang 2006-04-25 09:25 发表评论
]]>
巧用虚友元函?原创)http://www.shnenglu.com/ace/archive/2006/04/17/5796.htmlStone JiangStone JiangMon, 17 Apr 2006 14:12:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/17/5796.htmlhttp://www.shnenglu.com/ace/comments/5796.htmlhttp://www.shnenglu.com/ace/archive/2006/04/17/5796.html#Feedback3http://www.shnenglu.com/ace/comments/commentRss/5796.htmlhttp://www.shnenglu.com/ace/services/trackbacks/5796.html父类的友元不会自动成为子cȝ友元Q而且友元会破坏封装;C++的语方不允许Q非成员Q友元函Cؓ虚函数?br />但是Q某些时候,必须通过友元才能实现一些操作符重蝲Q如operator<<()Q如果ؓ每个子类都实现operator<<()倒是一个可行的ҎQ但是显得很啰嗦?br />
如果能把友元定义函数Q则子类可以l承该友元的接口而无需重复声明友好那该多好啊?
本文则通过一U变通的Ҏ巧妙辑ֈ虚函数的效果?br />
//基类 Base.



#pragma once
#include 
< iostream >
using   namespace  std;
class  Base
{
public :
  Base(
void );
  
~ Base( void );
public :
  
virtual   void  print(ostream &  output)  =   0 ;
  friend ostream
&   operator   << (ostream &  output,Base &  obj);
private :
  
char *  name_;
  
int  age_;
}
;

基类的实?base.cpp
#include "StdAfx.h"
#include 
".\base.h"

Base::Base(
void)
{
  name_ 
= "This is data1";
  age_ 
= 18;
}


Base::
~Base(void)
{
}


void Base::print(ostream& output)
{
  output
<<"name = " <<name_<<endl;
  output
<<"age = "<<age_<<endl;
}


ostream
& operator<<(ostream& output,Base& obj)
{
  obj.print(output);
  
return output;
}


zcȝ Derived.h

#pragma once
#include 
"Base.h"
class Derived :public Base
{
public:
  Derived(
int score = 80);
  
~Derived(void);
  
virtual void print(ostream& output);
private:
  
int score_;
}
;

zcȝ实现 Derived.cpp
#include "StdAfx.h"
#include 
".\derived.h"

Derived::Derived(
int score):score_(score)
{
  
}


Derived::
~Derived(void)
{
}


void Derived::print(ostream& output)
{
  Base::print(output);
  output
<<"score = "<<score_<<endl;
}


ȝ序main.cpp
// Test_VirtualFirendFunction.cpp : Defines the entry point for the console application.
//

#include 
"stdafx.h"
#include 
<iostream>
#include 
"Derived.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
  Derived  d;

  Derived  d2(
90);
  cout
<<d<<endl<<d2<<endl;
    
return 0;
}



屏幕输入l果
name = This is data1
age = 18
score = 80

name = This is data1
age = 18
score = 90

l果Q?br />M从BasezcȝQ都可以利用cout<<obj的机刉过进行输出?br />
同样道理Q也可以利用此方法实现ACE_InputCDR  / ACE_OutputCDR对网l操作序列化操作?br />(本文也是回答某网友关于派生类不能重蝲的问题的回复)



Stone Jiang 2006-04-17 22:12 发表评论
]]>
C++风格的类型{换的用法 (?http://www.shnenglu.com/ace/archive/2006/04/16/5641.htmlStone JiangStone JiangSun, 16 Apr 2006 06:21:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/16/5641.htmlhttp://www.shnenglu.com/ace/comments/5641.htmlhttp://www.shnenglu.com/ace/archive/2006/04/16/5641.html#Feedback1http://www.shnenglu.com/ace/comments/commentRss/5641.htmlhttp://www.shnenglu.com/ace/services/trackbacks/5641.html C++风格的类型{换的用法
q是More Effecitve C++里的W二条对cd转换讲的很好Q也很基好懂?br />Item M2Q尽量用C++风格的类型{?br />仔细xC卑贱的类型{换功能(castQ,其在E序设计中的Cpgoto语句一样o人鄙视。但是它q不是无法o人忍受,因ؓ当在某些紧要的关_cd转换q是必需的,q时它是一个必需品?br />不过C风格的类型{换ƈ不代表所有的cd转换功能?br />一 来它们过于粗鲁,能允怽在Q何类型之间进行{换。不q如果要q行更精的cd转换Q这会是一个优炏V在q些cd转换中存在着巨大的不同,例如把一个指?  const对象的指针(pointer-to-const-objectQ{换成指向非const对象的指针(pointer-to-non -const -objectQ?即一个仅仅去除const的类型{?Q把一个指向基cȝ指针转换成指向子cȝ指针Q即完全改变对象cdQ? 传统的C风格的类型{换不对上qCU{换进行区分。(q一点也不o人惊Ӟ因ؓC风格的类型{换是为C语言设计的,而不是ؓC++语言设计的)?br />? 来C风格的类型{换在E序语句中难以识别。在语法上,cd转换由圆括号和标识符l成Q而这些可以用在CQ+中的M地方。这使得回答象这样一个最基本的有 关类型{换的问题变得很困难:“在q个E序中是否用了cd转换Q”。这是因Zh工阅d可能忽略了类型{换的语句Q而利用象grep的工L序也不能? 语句构成上区分出它们来?br />C++通过引进四个新的cd转换操作W克服了C风格cd转换的缺点,q四个操作符?  static_cast, const_cast, dynamic_cast, 和reinterpret_cast? 在大多数情况下,对于q些操作W你只需要知道原来你习惯于这样写Q?br />(type) expression
而现在你d该这样写Q?br />static_cast
< type > (expression)
例如Q假设你x一个int转换成doubleQ以便让包含intcd变量的表辑ּ产生出QҎ值的l果。如果用C风格的类型{换,你能q样写:
int firstNumber, secondNumber;

double result = ((double)firstNumber)/secondNumberQ?br />如果用上q新的类型{换方法,你应该这样写Q?br />double result = static_cast
< double > (firstNumber)/secondNumber;
q样的类型{换不论是对h工还是对E序都很Ҏ识别?br />static_cast  在功能上基本上与C风格的类型{换一样强大,含义也一栗它也有功能上限制。例如,你不能用static_cast象用C风格的类型{换一h  struct转换成intcd或者把doublecd转换成指针类型,另外Qstatic_cast不能从表辑ּ中去除const属性,因ؓ 另一个新的类型{换操作符const_cast有这L功能?br />其它新的C++cd转换操作W被用在需要更多限制的地方。const_cast用于 cd转换掉表辑ּ的const或volatileness属性。通过使用const_castQ你向h们和~译器强调你通过cd转换惛_的只是改变一些东 西的 constness或者volatileness属性。这个含义被~译器所U束。如果你试图使用const_cast来完成修? constness 或者volatileness属性之外的事情Q你的类型{换将被拒l。下面是一些例子:
class Widget {  };
class SpecialWidget: public Widget {  };
void update(SpecialWidget *psw);
SpecialWidget sw; // sw 是一个非const 对象?br />const SpecialWidget& csw = sw; // csw 是sw的一个引?br />// 它是一个const 对象
update(
&csw ); // 错误!不能传递一个const SpecialWidget* 变量
// l一个处理SpecialWidget*cd变量的函?br />update(const_cast
< SpecialWidget * > ( &csw ));
// 正确Qcsw的const被显C地转换掉(
// csw和sw两个变量值在update
//函数中能被更斎ͼ
update((SpecialWidget*)
&csw );
// 同上Q但用了一个更难识?br />//的C风格的类型{?br />Widget *pw = new SpecialWidget;
update(pw); // 错误Qpw的类型是Widget*Q但?br />// update函数处理的是SpecialWidget*cd
update(const_cast
< SpecialWidget * > (pw));
// 错误Qconst_cast仅能被用在媄?br />// constness or volatileness的地方上?
// 不能用在向承子c进行类型{换?br />到目前ؓ止,const_cast最普通的用途就是{换掉对象的const属性?br />W? 二种Ҏ的类型{换符是dynamic_castQ它被用于安全地沿着cȝl承关系向下q行cd转换。这是_你能用dynamic_cast把指向基 cȝ指针或引用{换成指向其派生类或其兄弟cȝ指针或引用,而且你能知道转换是否成功。失败的转换返回空指针Q当Ҏ针进行类型{换时Q或者抛出异? Q当对引用进行类型{换时Q:
Widget *pw;

update(dynamic_cast
< SpecialWidget * > (pw));
// 正确Q传递给update函数一个指?br />// 是指向变量类型ؓSpecialWidget的pw的指?br />// 如果pw实指向一个对?
// 否则传递过ȝɽI指针?br />void updateViaRef(SpecialWidget& rsw);
updateViaRef(dynamic_cast
< SpecialWidget & > (*pw));
//正确。传递给updateViaRef函数
// SpecialWidget pw 指针Q如果pw
// 实指向了某个对?br />// 否则抛出异?br />dynamic_casts在帮助你览l承层次上是有限制的。它不能被用于缺乏虚函数的类型上Q参见条ƾM24Q,也不能用它来转换掉constnessQ?br />int firstNumber, secondNumber;

double result = dynamic_cast
< double > (firstNumber)/secondNumber;
// 错误Q没有承关p?br />const SpecialWidget sw;

update(dynamic_cast
< SpecialWidget * > ( &sw ));
// 错误! dynamic_cast不能转换
// 掉const?br />如你惛_没有l承关系的类型中q行转换Q你可能惛_static_cast。如果是Z去除constQ你d用const_cast?br />q四个类型{换符中的最后一个是reinterpret_cast。用这个操作符的类型{换,其的转换l果几乎都是执行期定义(implementation-definedQ。因此,使用reinterpret_casts的代码很隄植?br />reinterpret_casts的最普通的用途就是在函数指针cd之间q行转换。例如,假设你有一个函数指针数l:
typedef void (*FuncPtr)(); // FuncPtr is 一个指向函?br />// 的指针,该函数没有参?br />// q回值类型ؓvoid
FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳
// 10个FuncPtrs指针的数l?br />让我们假设你希望Q因为某些莫名其妙的原因Q把一个指向下面函数的指针存入funcPtrArray数组Q?br />int doSomething();
你不能不l过cd转换而直接去做,因ؓdoSomething函数对于funcPtrArray数组来说有一个错误的cd。在FuncPtrArray数组里的函数q回值是voidcdQ而doSomething函数q回值是intcd?br />funcPtrArray[0] = 
&doSomething;  // 错误Q类型不匚w
reinterpret_cast可以让你qɾ~译Kq?/span>


Stone Jiang 2006-04-16 14:21 发表评论
]]>
今天C几本?有没有正看这几本书的朋友?http://www.shnenglu.com/ace/archive/2006/04/15/5610.htmlStone JiangStone JiangSat, 15 Apr 2006 05:09:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/15/5610.htmlhttp://www.shnenglu.com/ace/comments/5610.htmlhttp://www.shnenglu.com/ace/archive/2006/04/15/5610.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/5610.htmlhttp://www.shnenglu.com/ace/services/trackbacks/5610.html

Stone Jiang 2006-04-15 13:09 发表评论
]]>
今天喜得两本?需要的Lahttp://www.shnenglu.com/ace/archive/2006/04/14/5514.htmlStone JiangStone JiangFri, 14 Apr 2006 02:14:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/14/5514.htmlhttp://www.shnenglu.com/ace/comments/5514.htmlhttp://www.shnenglu.com/ace/archive/2006/04/14/5514.html#Feedback5http://www.shnenglu.com/ace/comments/commentRss/5514.htmlhttp://www.shnenglu.com/ace/services/trackbacks/5514.htmlC++ Coding Standards: 101 Rules, Guidelines, and Best PracticesBy Herb Sutter, Andrei Alexandrescu
q本书得?005 Jolt大奖

2. The Art or Unix Programming
q是lxxx和yyyy以来的最优秀的Unixȝ.



Stone Jiang 2006-04-14 10:14 发表评论
]]>
通过模板实现"多态?,避开l承带来的性能开销之讨?http://www.shnenglu.com/ace/archive/2006/04/14/5506.htmlStone JiangStone JiangFri, 14 Apr 2006 01:29:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/14/5506.htmlhttp://www.shnenglu.com/ace/comments/5506.htmlhttp://www.shnenglu.com/ace/archive/2006/04/14/5506.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/5506.htmlhttp://www.shnenglu.com/ace/services/trackbacks/5506.html多态生其实q可以通过cȝ聚合或组合的方式来达?wbr>,从而还可以避免l承或多l承,
代码如下 (未在开发环境中调试)
#include <stdio.h> 

template 
<class T> 
class A  

public
  
void print() 
  { 
    base_.b(); 
  }   
  typename T base_;
}; 


class B 

public
  
void b() 
  { 
    printf(
" Class B---------------------\n"); 
  }   
}; 

class C 

public
  
void b() 
  { 
    printf(
" Class C---------------------\n"); 
  }   
}; 

int main(intchar* []) 

  A
<B> c; 
  c.print(); 
  A
<C> c2;
  c2.print();
  
return 0;   




Stone Jiang 2006-04-14 09:29 发表评论
]]>
? UDP实现可靠文g传输 (?http://www.shnenglu.com/ace/archive/2006/04/13/5465.htmlStone JiangStone JiangThu, 13 Apr 2006 05:56:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/13/5465.htmlhttp://www.shnenglu.com/ace/comments/5465.htmlhttp://www.shnenglu.com/ace/archive/2006/04/13/5465.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/5465.htmlhttp://www.shnenglu.com/ace/services/trackbacks/5465.html 

                                     

用UDP实现可靠文g传输


大家都清楚,如果用TCP传输文g的话Q是很简单的Q根本都不用操心会丢包,除非是网l坏了,得重来。用UDP的话Q因为UDP是不可靠的,所以用它传输文Ӟ要保证不丢包Q就得我们自己写额外的代码来保障了。本文就说说如果保证可靠传输?br />

   要实现无差错的传输数据,我们可以采用重发hQARQQ协议,它又可分lARQ协议、选择重发ARQ协议、滑动窗口协议。本文重点介l滑动窗口协议,其它的两U有兴趣的可参考相关的|络通信之类的书?br />

    采用滑动H口协议Q限制已发送出M未被认的数据的数目。@环重复用已收到的那些数据的序受具体实现是在发送端和接收端分别讑֮发送窗口和接收H口?br />

 Q?Q发送窗?br />

  发送窗口用来对发送端q行量控制。发送窗口的大小Wt代表在还没有收到Ҏ认的条件下Q发送端最多可以发送的数据帧的个数。具体意思请参考下图:


 Q?Q接收窗?br />

  接收H口用来控制接收数据帧。只有当接收到的数据帧的发送序可在接收窗口内Q才允许该数据帧收下,否则一律丢弃。接收窗口的大小用Wr来表C,在连lARQ协议中,Wr = 1。接收窗口的意义可参考下图:


  在接收窗口和发送窗口间存在着q样的关p:接收H口发生旋{后,发送窗口才可能向前旋{Q接收窗口保持不动时Q发送窗口是不会旋{的。这U收发窗口按如此规律时钟方向不断旋转的协议q法ؓ滑动H口协议?br />

   好了Q在上面Ҏ动窗口协议有大致了解后,我们q是q入正题吧:Q?br />

   发送端的发送线E:


   int  ret;


   int  nPacketCount = 0;


   DWORD dwRet;


   SendBuf sendbuf;


   DWORD dwRead;


   DWORD dwReadSize;




   SendBuf* pushbuf;




   //计算一pȝ文gơ数Q若文g已读完,但客L没有接收完,


   //则要发送的内容不再从文仉dQ而从m_bufqueue里提?br />

   nPacketCount = m_dwFileSize / sizeof(sendbuf.buf);




  //若不能整除,则应?


  if(m_dwFileSize % sizeof(sendbuf.buf) != 0)


     ++nPacketCount;


 SetEvent(m_hEvent);

 




  CHtime htime;




  //若已发送大小于文件大ƈ且发送窗口前沿等于后沿,则l发?br />

  //否则退出@?br />



 if(m_dwSend 
<  m_dwFileSize )  // 文g没有传输完时才l传?br />

 {


  while(1)


  {


   dwRet 
= WaitForSingleObject(m_hEvent,  1000);


   if(dwRet 
==  WAIT_FAILED)


   {


    return false;


   }


   else if(dwRet 
==  WAIT_TIMEOUT)


   {


    //重发


    ::EnterCriticalSection(&m_csQueue);  // q入m_bufqueue的排斥区


    ret 
= m_hsocket.hsendto((char*)m_bufqueue.front(),  sizeof(sendbuf));


    ::LeaveCriticalSection(&m_csQueue);  // 退出m_bufqueue的排斥区


    if(ret 
==  SOCKET_ERROR)


    {


     cout << "重发p|Ql重? << endl;


     continue;


    }


   ResetEvent(m_hEvent);

 


    continue;


   }


  //若发送窗口大?lt; 预定大小 && 已读文gơ数(nReadIndex) < 需要读文g的次?nReadCount)Q则l箋d发?br />
 


   //否则Q要发送的内容从m_bufqueue里提?br />

   if(m_dwSend < m_dwFileSize)


   {


    dwReadSize 
= m_dwFileSize  - m_dwSend;


    dwReadSize 
= dwReadSize  < MAXBUF_SIZE ?   dwReadSize :MAXBUF_SIZE;




    memset(sendbuf.buf, 0, sizeof(sendbuf.buf));


    if(!ReadFile(m_hFile, sendbuf.buf, dwReadSize, &dwRead, NULL))


    {


     //AfxMessageBox("d文gp|,L认文件存在或有读取权?");


     cout << "d文gp|,L认文件存在或有读取权?" << endl;


     return false;


    }




    m_dwSend +
= dwRead;




    
sendbuf.index  = m_nSendIndexHead;


    
// 发送窗口前沿向前移一格     m_nSendIndexHead  = (m_nSendIndexHead  + 1) %   Sliding_Window_Size; 




  sendbuf.dwLen 
= dwRead;

 


  
//保存发送过的数据,以便重发

 


   ::EnterCriticalSection(&m_csQueue);   // q入m_bufqueue的排斥区


   pushbuf 
= GetBufFromLookaside();


   
memcpy(pushbuf, &sendbuf, sizeof(sendbuf));



   m_bufqueue.push(pushbuf);



   // 文g已读完,在队列中加一File_End标志Q以便判断是否需要l发送    if(m_dwSend 
> = m_dwFileSize)    



   {


     pushbuf = GetBufFromLookaside();


    pushbuf->index = File_End;

 


     pushbuf->dwLen = File_End;


     memset(pushbuf->buf, 0, sizeof(pushbuf->buf));


     m_bufqueue.push(pushbuf);



    }


    ::LeaveCriticalSection(
&m_csQueue );   // 退出m_bufqueue的排斥区


   }


   ::EnterCriticalSection(
&m_csQueue );    // q入m_bufqueue的排斥区

 


   // 所有数据包已发送完?退出@环    if(m_bufqueue.front()->index == File_End)  



   {


     ::LeaveCriticalSection(
&m_csQueue );   // 退出m_bufqueue的排斥区


     break;


   }


   // 发送窗口小于指定|l箋发送    else if(m_bufqueue.size() 
< = Send_Window_Size



   {


    ret 
= m_hsocket.hsendto((char*)m_bufqueue.front(),  sizeof(sendbuf));


    if(ret 
==  SOCKET_ERROR)


    {


     ::LeaveCriticalSection(&m_csQueue);  // 退出m_bufqueue的排斥区


     cout << "发送失败,重发" << endl;


     continue;


    }




    //延时Q防止丢?br />

    Sleep(50);  


   }


   else           // 发送窗口大于指定|{持接收U程接收认消息


   {


    ResetEvent(m_hEvent);


   }


   ::LeaveCriticalSection(&m_csQueue);    // 退出m_bufqueue的排斥区


  }


 }


  发送端的接收线E:



  int ret;


  RecvBuf recvbuf;




  while(m_hFile !
= NULL)


  
{


    ret 
= m_hsocket.hrecvfrom((char*)&recvbuf,  sizeof(recvbuf));  


    if(ret 
==  SOCKET_ERROR)


    {


      //AfxMessageBox("接收认消息出错");


      ::EnterCriticalSection(&m_csQueue);


      if(m_bufqueue.front()-
> index == File_End) // 文g传输完毕


      {


         ::LeaveCriticalSection(
&m_csQueue );


         break;


       }


       ::LeaveCriticalSection(
&m_csQueue );


       cout 
< < "接收认消息出错:  " << GetLastError() << endl;



       return false;


     }


  if(recvbuf.flag 
==  Flag_Ack && recvbuf.index  ==     m_nSendIndexTail)

  


   {


      m_nSendIndexTail 
= (m_nSendIndexTail  + 1) % Sliding_Window_Size;


      //该结点已得到认Q将其加入旁视列表,以备再用


      ::EnterCriticalSection(&m_csQueue);


      m_bufLookaside.push(m_bufqueue.front());


      m_bufqueue.pop();


     ::LeaveCriticalSection(&m_csQueue);


     SetEvent(m_hEvent);



   }


 }




接收端的接收U程Q?br />

 int  ret;


 DWORD dwWritten;


 SendBuf recvbuf;


 RecvBuf sendbuf;


 int nerror 
= 0;


 
// 讄文g指针位置Q指向上ơ已发送的大小



 SetFilePointer(m_hFile, 0, NULL, FILE_END);


 //若已接收文g大小于需要接收的大小Q则l箋



 while(m_dwSend < m_dwFileSize)


 {


  //接收


  memset(&recvbuf, 0, sizeof(recvbuf));


  ret 
= m_hsocket.hrecvfrom((char*)&recvbuf,  sizeof(recvbuf));


  if(ret 
==  SOCKET_ERROR)


  {


   return false;


  }


  //不是希望接收的,丢弃Ql接?br />


  if(recvbuf.index !
= (m_nRecvIndex)  % Sliding_Window_Size)


  {


   nerror++;


   cout << recvbuf.index << "error?" << m_nRecvIndex << endl;


   continue;


  }


  if(!WriteFile(m_hFile, recvbuf.buf, recvbuf.dwLen, &dwWritten, NULL))



  {


   //AfxMessageBox("写入文gp|");


   cout << "写入文gp|" << endl;


   return false;


  }




  //已接收文件大?br />

  m_dwSend +
= dwWritten;


  
//发送确认消?br />


  sendbuf.flag 
= Flag_Ack;


  
sendbuf.index  = m_nRecvIndex;




  
ret  = m_hsocket.hsendto((char*)&sendbuf,  sizeof(sendbuf));


  if(ret 
==  SOCKET_ERROR)


  {


   return false;


  }


  //接收H口前移一?br />


  m_nRecvIndex 
= (m_nRecvIndex  + 1) % Sliding_Window_Size;


 }



 


 


Stone Jiang 2006-04-13 13:56 发表评论
]]>
UML培训提纲初步http://www.shnenglu.com/ace/archive/2006/04/13/5461.htmlStone JiangStone JiangThu, 13 Apr 2006 05:27:00 GMThttp://www.shnenglu.com/ace/archive/2006/04/13/5461.htmlhttp://www.shnenglu.com/ace/comments/5461.htmlhttp://www.shnenglu.com/ace/archive/2006/04/13/5461.html#Feedback0http://www.shnenglu.com/ace/comments/commentRss/5461.htmlhttp://www.shnenglu.com/ace/services/trackbacks/5461.html 课时按排
  W一?br />    Z么要用UML
      问题
        目前面的问?br />        业界共同的问?br />      问题Ҏ的所?br />      UML产生的背?br />      UML的积极意?br />      使用UML的时?br />        UML图一?br />      不仅仅是UML
        UP(l一q程)
          RUP
          XP
          FDD
          etc..
        需求工E?br />        OO技?br />          模式
          ZOO的重?br />          Z模式的框?br />    后箋评安排
      W二?br />        需?br />      W三?br />        分析和设?br />      更多主题
  W二?br />    用UML需求分析基
      需求工E基
        需求模型理?br />        文档化需?br />        理需?br />        理需求变?br />      用例技术基
        表达愿景
        故事和场?br />        识别pȝ边界
        识别主角
        识别用例
        撰写用例规约
        ȝ例图
        用例之间的关p?br />        用用例描q需?br />  W三?br />    用UMLq行单系l设?br />      pȝ设计一般原?br />      cd基础
      序囑֟
      l常犯的设计错误
  W四?br />    UMLq阶
目标
  学会用基本的UML撰写和阅读设计文?br />    使受训者能撰写比较有合格的设计文档
    使受训者能阅读设计文档


思维导图

UML培训 2.0.jpg

Stone Jiang 2006-04-13 13:27 发表评论
]]>
þþWWW| þһձɫۺϾþ| þSEƷһ| þþþӰԺС | þþþþþƷͼƬ| þþþþùƷŮ| ƷþþĻ| 㽶99þùۺϾƷլ| þþ뾫Ʒպý| պ뾫Ʒþþò| þ۲ӰԺѿҹɫ| ˾þ91| 99ƷþþƷһ| 97þþþƷۺ88þ| þùƷ99Ʒ| þ㽶߿ۿ| þרƷ| Ʒþþþþþ| þƬѹۿ| ƷþþþjkƷ| þþƷһӰԺ| VۺVŷþ| ݺɫþۺ_ | ޾ƷŮþþþ99| 91þ㽶Ů߿| þþþþžžƷӰԺ| վþþ| Ʒþþþa| þۺ97ɫֱ| þԭƷ| ŷþþþþ| ŷһþþþþþôƬ| þþWWWëƬ| ݺݾþۺ˲| һaƬþëƬ| ޾Ʒһۺ99þ| þۺav| ˾þùѹۿƵ | Ʒ׾þAAAƬ69| þþþ޾Ʒվ | ޾Ʒþþþþ|