2009年12月17日星期四

换位

毕业时找工作,几次面试的感觉很是不爽。
昨天换了位置,第一次面试别人,于是可以不再单方面的看待面试这一过程了。
应聘者如何表现出自己的技能,面试者如何给应聘者机会来展示自己,这确实需要仔细思考。

最近项目组比较缺人。没有准备的时候,被同事拉去面试一个应聘者,完全不了解任何情况,我跟同事前往洽谈室。路上简单向同事问了下基本要求,然后去人力资源那里拿了份简历看了看。应聘者简单自我介绍后,同事开始跟他谈一些他过去项目相关的问题,我借机把简历仔细看了一遍,接着他们的话题插了几句。最后,问了几个简单的技术问题。

其实我一直觉得面试应该是有一个提纲的,或者更死板一点儿,应该有若干预备问题的,这样的一个提纲,从不同方面来考察一个应聘者的表现。
从面试别人我才认识到介绍项目时对于自己在项目中的工作,以及遇到的重点和难点的解决过程的说明是非常重要的。

究竟需要从哪些方面来考察一个应聘者呢?
我想对于项目组的应用开发来说,从应聘者对软件开发流程的理解、对所用开发工具的熟悉程度、以及对基础概念的了解几方面看就可以了。

2009年12月14日星期一

Detach from Program

一个程序出问题了,我用Delphi附加到远程进程进行调试,下班的时候,我想把本地机器关掉,让远程的程序继续运行。无奈不知怎么脱离开调试的程序。

查看Delphi帮助,明明确确的看到了一条Detach from Program

Choose Run|Detach from Program to tell the debugger that you no longer want to debug a process. It is different from Program Reset in that Reset kills the process you're debugging. Detach from Program effectively ends the debug session on the current process, but it leaves the process running (the debugger no longer controls the process).

但是到菜单里一看,根本就没有Detach from Program这个菜单项嘛,帮助文件睁眼说瞎话?

求助于AQTime看看,结果看到FAQ里这样一条:

Q.: How do I detach from the process that I attached to earlier? 
A.: Currently, detaching is not supported. When attaching, AQtime instruments the process's binary code and these changes cannot be undone.

好嘛,What done cannot be undone。

2009年10月29日星期四

准命令行编译Flash

没用过Flash,但是项目中有Flash的工程,当然希望Flash的编译成为自动构建的一部分。
本来以为可以用Flex SDK来搞定的,但是构建人员没有搞出来,只好回归到jsfl先用这准命令行的方式吧。
还是先放代码

//demo.jsfl
var doc = fl.openDocument("file:///c:/demo.fla");
doc.exportSWF("file:///c:/demo.swf", true);
fl.quit(false);

再加个命令行
flash.exe demo.jsfl 

有些时候,简单解决问题就好。

Invalid floating point operation

诡异事件的出现,多数是由于对其了解不够多。
本来在C++中调用多次的dll,放到Delphi中调用,总是产生“”Invalid floating point operation”异常,很是诧异。
多方搜索,原来要关闭浮点异常。

先放一段Delphi帮助中的例子,恐怕有很多人忘了用完之后设置回去。
procedure NumericExample;
var
  Saved8087CW: Word;
begin
  Saved8087CW := Default8087CW;
  Set8087CW($133f); { Disable all fpu exceptions }
  ThirdPartyRoutine;
  Set8087CW(Saved8087CW);
end;

Delphi帮助中的关于Set8087CW的说明,被我重新编排了一下引用如下(三段话,顺序正好与原来相反)。
It is recommended that you disable all floating-point exceptions when using OpenGL to render 3D graphics. To do this, call Set8087CW(0x133f) in your main form� OnCreate event before calling any OpenGL functions.
This routine allows the programmer to have direct access to the CW. Be aware that using this routine to change the value of the 8087CW will change the behavior of the program� FP calculations. It is the programmer's responsibility to reset it.
The floating-point unit control word controls the precision of floating point calculations, the rounding mode, and whether certain floating-point operations trigger exceptions. See Intel's processor documentation for details.
 
再放一段搜索来的内容增加长度。
Tripping floating-point exceptions
Floating-point exceptions are, as you might expect by virtue of the name, rare. The ones that happen most commonly by mistake in my experience are the zero-divide and invalid operation exceptions. Zero divide tends to happen whenever you have an unchecked normalization operation, such as resetting a 2D or 3D vector to unit length — which works fine, until someone hands you a vector of length zero. Another example would be trying to normalize a portion of audio that was totally silent. When the zero-divide exception is masked, the FPU spits out a signed infinity instead, which sometimes works out in the end. For instance, if the expression is of the form |x/y| > n, then the infinity would give you the correct result.
Invalid operation exceptions are more serious and result from operations that don't have a graceful way to degrade, such as 0/0, the square root of -1, etc. These too often result from the lack of bounds checks. For instance, a common way to determine the angle between two vectors is through dot product, since the shortest angle between two vectors is acos(dot(v1 / |v1|, v2 / |v2|)). Unfortunately, the common way of normalizing vectors is to multiply by the reciprocal square root of the squared length (dot(v,v)), which can give you a not-quite-unit-length vector since the squaring operation discards half of the usual precision. This can then lead to taking the arccosine of a number slightly larger than 1. When such an operation occurs and invalid operation exceptions are masked, the FPU spits out a Not a Number (NaN) value and keeps going. You can also trip such an exception by trying to operate on NaNs, especially by loading garbage data that isn't a valid IEEE finite number.
In general, you don't want to be tripping floating-point exceptions, even if they are masked. The reason is that when the FPU hits one, the fast hardware can't handle it and punts to the microcode, which then takes about twenty times longer. This is especially bad with NaNs since any operation with a NaN produces another NaN, causing them to spread throughout your calculations (NaN disease) and slow down everything massively. You can even crash due to NaNs blowing past clamp expressions, since any comparison with a NaN is false and converting one to integer form results in integer indefinite (0x80000000). Despite the erroneous results, though, NaNs can appear sporadically in a large Win32 program without anyone knowing, and may go unnoticed in a code base for years.
Note that although exceptions are really slow and usually indicate mistakes, the results when the exceptions are masked are well-defined. It is possible, and sometimes reasonable, to actually depend on and test for specific results from masked exceptions. So it isn't valid to simply say "don't do that."

 

2009年10月26日星期一

八小时里的那点儿事儿

又开始一个新项目了,工期不会超过两个月,代码不过万把行。
每个项目都是所谓工期短,时间紧,公司领导很重视。结果就是造就了一堆没人用的小玩意儿。
每个产品后期都会随着一次次的产品评审改来改去,其实那最应该叫做需求评审。
每个领导都有自己的想法,会对产品提出改进意见,但是,如果一个领导需要为界面上的几个普通的元素亲自负责的话,那是不是应该先招聘几个设计师呢。
每次例会都是以这些问题很重要开始,以这些问题会后讨论结束。

2009年9月10日星期四

Be Careful

连续两天到客户现场去测试程序,由开发、测试、技术支持三队人马一起行动。显然,这种情况需要事先安排好计划,到了现场以后才可能有条不紊的进行工作,否则,只能是一团糟或者延误时间。

今天自己犯了一个太大的错误,在现场以为数据库里都是些测试数据,直接把某个表的数据全部UPDATE成了初始状态,谁知里面有一万多条客户真实数据,幸好客户的这批任务已全部结束,否则不堪设想。事情虽然没有产生什么恶性后果,但这样的错误难以忍受。以后做类似操作,一定要确保头脑清醒,且谨慎!

2009年8月31日星期一

To be, or not to be


我写了一些自动化脚本,为了在C/S结构软件中编写服务端时,有个客户端可以自已运行,省得一心二用,左顾右盼。我自己用到开发过程中,这个小东西效果很好,省了很多事。
它看起来确实不错呀,项目组长发现了,推荐给测试人员吧。我摇身一变,从开发变成测试开发了。但是GUI测试确实烦人呀,好歹将就着能用了,我真希望到此为止吧。但是,用户的需求是不断的,然而,我希望这些工作应该是由测试人员来做的。因为,工具只是辅助工作的,只有使用他的人才知道怎么最有效。
不能说服别人做好,如果真的做的好,别人自然会按你做的来;同样,我也不希望把东西做好了送到别人跟前让人承认这是好东西。

我把桌面的AutoIt背景换掉了,到此为止。附加的一个原因是,没有发现AutoIt中动态调用函数的方式,再做更多工作需要的代价过高了。

2009年8月18日星期二

WTL及其他


WTL有更新了,8.1,在VS2008中安装Wizard时再也不会复制-打开-替换-保存-关闭操作了。
WTL程序中,包含ActiveX控件要导入类型库,在stdafx.h中加入
#import "c:/windows/system32/wmp.dll" raw_interfaces_only, raw_native_types, no_namespace, named_guids
Flash控件可能不在system32下了,跑到Macromed中的某个地方了。

包含RichEdit控件,要在创建前进行
HINSTANCE hInstRich = ::LoadLibrary(CRichEditCtrl::GetLibraryName());
ATLASSERT(hInstRich != NULL);
否则主窗体创建失败。 
结束前
::FreeLibrary(hInstRich); 

Subversion也支持external文件了,但是还存在已知问题,所以多数时候还是没用,因为我想external的多数是二进制文件。

好长时间没注意,FOP也更新到0.95了。

2009年8月12日星期三

DON'T DO THIS


Delphi的帮助中有一个条目是Returning a PChar local variable,当然这个条目说的是反例,是说长字符串与PChar之间的差别会引起的问题。在这个条目中所举的例子里,特别注释了“// DON'T DO THIS”,但是正如这一条目开头便指出的,这却是一个常见的错误,所以这样写法还是屡见不鲜,而且通常看起来也似乎正确,但是,并不是这样的。

function ReturnLocalStringToPChar: PChar;
   var S, Y: string;
   begin
      S := 'aaa';
      Y := 'bbb';
      S := S + Y;
      Result := PChar(S);
   end;

   procedure Test(S: string)
   var
     A: string;
     P: PChar;
  begin
     P := ReturnLocalStringToPChar;
     WriteLn(P);
     A := GetCurrentDir;
     WriteLn(P);
  end;

如果你认为两次打印的内容是一样的,那就错了。

所以,文档是一定要看的。想起邮件列表中有人回答问题的同时说不知道为什么很多人不看文档,而他对很熟悉的东西也是要常翻翻文档的。
同样,在前几天,在同别人调试AutoIt3脚本时,发现因为没有看到最新的文档,对返回值的判断不正确的情况。

看清楚文档,用明确知道的东西,知道自己在写什么,才可能写出可靠的代码。

2009年7月17日星期五

Out of Memory 就这样崩溃

一年多的短信从来没有删除过,手机终于崩溃了,任何动作都提示空间不足。
我也一样,一年过去了,就这样静静的崩溃,做不出任何动作。
Formatting的是手机。格式化的是我。
无声无息。无知无觉。

行动 Just Do It

行动是最重要的。今天被自己的行为教育了。
半年前,为了熟悉HMM,将别人的一个英文词性标注器用Python实现了一遍,无非是在C++和Python间翻译代码。可是,翻译完了一测试,准确率不足70%,毫无疑问,肯定是自己的实现某处有不显眼的错误。当时没有找出问题所在,后来多次打算把这个调试好,以备后用,但每次瞅几眼那些代码就困意重重而作罢。今天中午终于打定主意,仔细调试了一遍代码,不到二十分钟就找出来了问题,修改后已知词准确率98%。其实,跟以前相比,差别就是多了一点儿行动与耐心。
想起一位科学家谈到自己的重大发现时轻描淡写的说,只要做实验,谁都能看到。
只有行动才可能有结果;只有付出才可能有收获。

2009年5月14日星期四

南京小聚

几个人从不同的方向到南京,不顾劳累,放弃游玩,来小聚一场。

上学时起租房的老朋友已几年不见,饭桌上突然说起魔方的时候,只以为是他一贯的调侃作风;当他真的从背包里拿出 Rublk's Cube 的时候,我也笑着拿出了自己的魔方,我们再一次握手。



南京几位东家的盛情款待让我感到情意的深厚,这是多么难得的开心时刻。


2009年4月26日星期日

魔方聚会

在某个KFC小聚。还好直到两三个小时后才被“请”出来。
见到了各式各样的魔方爱好者和各式各样的魔方。

2009年4月16日星期四

Lose Count 数不清


我用针尖计算着与你见面的日子,一次又一次的麻木与苏醒,不断忘了数目又从零开始。
一夜中睡了三次,起来三次,我不知道该怎么计算。
这些混乱的日子,无休无止,我数不清。


Do we really need to count?

有时候简单的数几个数也总是数不对,计算机如此擅长此事,而我们却没有给以足够的信赖。
TCountState = class
  private
    Xes: TList;
    FXCount: Integer;
  public
    procedure AddX(X: XClass);
    procedure Reset;
  property XCount: Integer read FXCount;
end;

TCountState.AddX(X: XClass);
begin
  if not Xes.Has(X) then
  begin
    Xes.Add(X);
    Inc(XCount);
  end;
end;

TCountState.Reset;
begin
  XCount := 0;
  // However, nothing be done with Xes here.
end;

毫无疑问,在这千头万绪的岁月中,AddX()会在不同的线程中调用,即使算上脚指头,我们也无法数清楚XCount到底是什么了。好歹我们知道有个东西叫InterLockIncrement
很好,似乎学会数数了,但是,看起来有时候还是会数错,尤其是在一次次的麻木与苏醒之后,也许想忘记过去,却不够彻底,忘了一些东西而记住了另一些东西,这会混乱不堪。所以,在醒来之前把过去消除掉吧,它可能是美好的回忆,但也可能是沉重的包袱,或者兼而有之。
Xes.Clear;这一次,可以坚信不用手指头也可以正确数数了。

等等,我们真的需要数吗?
这些混乱的日子,无休无止,是度日如年、一日三秋还是十年一梦,谁也数不清,只有日历最清楚。
还是直接问问它们吧。
TCountState = class
  private
    Xes: TList;
    function GetXCount: Integer;
  public
    procedure AddX(X: XClass);
    procedure Reset;
  property XCount: Integer read GetXCount;
end;

TCountState.AddX(X: XClass);
begin
  if not Xes.Has(X) then
  begin
    Xes.Add(X);
  end;
end;

TCountState.GetXCount;
begin
  Result := Xes.Count;
end;

TCountState.Reset;
begin
  Xes.Clear;
end;

数数真的很难,曾经QQ空间这样跟我说
共 126 个签名,第 22 页/共 21 页
曾经我们在邮件列表中这样计算
K = 12
AVE = 23.45


k=13
average=23.57

k=13
AVG=23.49

出现了互斥问题,修正一下当前k=14,ave=23.60

2009年4月15日星期三

思维与行为的灵魂


两本好书。
一本是The Pragmatic Programmer ,道理不只为程序设计而言。文章也写得很好,言简意赅,读来学英文也很不错。这不得不让我想起了一本商业英语写作与演讲的书 Plain English at Work ,讲述了如何使用简洁的英文高效的传递信息,而书本身就是一个极佳的范例。前一本书远不止对程序设计有益处,而后一本也远不止对写作演讲有帮助。代码和文章的字里行间处处是思维与行为的灵魂,绝不仅仅是技巧与规则的躯壳。


1、关心你的技艺 
Care About Your Craft 
除非你在乎能否漂亮地开发出软件,否则其它事情都是没有意义的。 

2、思考!你的工作 
Think!About Your Work 
在你做某件事情的时候思考你在做什么。不间断地思考,实时地批判你的工作。这将占据你的一些宝贵时间,酬劳则是更为活跃地参与你喜爱的工作、感觉到自己在掌握范围日增的各种主题以及因感受到持续的进步而欢愉。从长远来说,你在时间上的投入将会随着你和你的团队变得更为高效、编写出更易于维护的代码以及开会时间的减少而得到回报。 


3、提供各种选择,不要找蹩脚的借口 
Provide Options,Don't Make Lame Excuses 
不要说事情做不到;要说明能够做什么来挽回局面。不要害怕提出要求,也不要害怕承认你需要帮助。 

4、不要容忍破窗户 
Don't Live With Broken Windows 
不要留着“破窗户”(低劣的设计、错误的决策、或者糟糕的代码)不修。发现一个就修一个。如果没有足够的时间进行适当的修理,采取某种行动防止进一步的破坏,并说明情势处在你的控制之下。 
如果你发现你所在团队和项目的代码十分漂亮——编写整洁、设计良好,并且很优雅,你不会想成为第一个弄脏东西的人。 

5、做变化的催化剂 
Be a Catalyst for Change 
你不能强迫人们改变。相反,要向他们展示未来可能会怎样,并帮助他们参与对未来的创造。 
设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,让他们大吃一惊。然后说:“要是我们增加...可能就会更好。”假装那并不重要。坐回椅子上,等着他们开始要你增加你本来就想要的功能。人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围。 

6、记住大图景 
Remember the Big Picture 
如果你抓一只青蛙放进沸水里,它会一下子跳出来。但是,如果你把青蛙放进冷水里,然后慢慢加热,青蛙不会注意到温度的缓慢变化,会呆在锅里,直到被煮熟。 
不要像青蛙一样。留心大图景。要持续不断地观察周围发生的事情,而不只是你自己在做的事情。 

7、使质量成为需求问题 
Make Quality a Requirements Issue 
你所制作的系统的范围和质量应该作为系统需求的一部分规定下来。让你的用户参与权衡,知道何时止步,提供足够好的软件。 

8、定期为你的知识资产投资 
Invest Regularly in Your Knowledge Portfolio 

让学习成为习惯。 
持续投入十分重要。一旦你熟悉了某种新语言或新技术,继续前进,学习另一种。 
是否在某个项目中使用这些技术,或者是否把它们放入你的简历,这并不重要。学习的过程将扩展你的思维,使你向着新的可能性和新的做事方式拓展。思维的“异花授粉”十分重要;设法把你学到的东西应用到你当前的项目中。即使你的项目没有使用该技术,你或许也能借鉴一些想法。例如,熟悉了面向对象,你就会用不同的方式编写纯C程序。 
如果你自己找不到答案,就去找出能找到答案的人。不要把问题搁在那里。 
 

9、批判地分析你读到的和听到的 
Critically Analyze What You Read and Hear 
不要被供应商、媒体炒作、或教条左右。要依照你自己的看法和你的项目的情况去对信息进行分析。 

10、你说什么和你怎么说同样重要 
It's Both What You Say and the Way You Say It 

作为开发者,我们必须在许多层面上进行交流。我们的时间有很大部分都花在交流上,所以我们需要把它做好。 
如果你不能有效地向他人传达你的了不起的想法,这些想法就毫无用处。 
知道你想要说什么;了解你的听众;选择时机;选择风格;让文档美观;让听众参与;做倾听者;回复他人。 
交流越有效,你就越有影响力。 
 

11、DRY原则——不要重复你自己 
DRY - Don't Repeat Yourself 
系统中的每一项知识都必须具有单一、无歧义、权威的表示。与此不同的做法是在两个或更多地方表达同一事物。如果你改变其中一处,你必须记得改变其它各处。这不是你能否记住的问题,而是你何时忘记的问题。 

12、让复用变得容易 
Make it Easy to Reuse 
你要做的是营造一种环境,在其中要找到并复用已有的东西,比自己编写更容易。如果复用很容易,人们就会去复用。而如果不复用,你们就会有重复知识的风险。 

13、消除无关事物之间的影响 
Eliminate Effects Between Unrelated Things 
我们想要设计自足(self-contained)的组件:独立,具有单一、良好定义的目的。如果组件是相互隔离的,你就知道你能够改变其中一个,而不用担心其余组件。只要你不改变组件的外部接口,你就可以放心:你不会造成波及整个系统的问题。 
你得到两个主要好处:提高生产率与降低风险。 

14、不存在最终决策 
There Are No Final Decisions 
没有什么永远不变——而如果你严重依赖某一事实,你几乎可以确定它将会变化。与我们开发软件的速度相比,需求、用以及硬件变得更快。通过DRY原则、解耦以及元数据的使用,我们不必做出许多关键的、不可逆转的决策。有许多人会设法保持代码的灵活性,而你还需要考虑维持架、部署及供应商集成等领域的灵活性。 

15、用曳光弹找到目标 
Use Tracer Bullets to Find the Target 
曳光弹能通过试验各种事物并检查它们离目标有多远来让你追踪目标。 
曳光弹代码含有任何一段产品代码都拥有的完整的错误检查、结构、文档、以及自查。它只不过功能不全而已。但是,一旦你在系统的各组件之间实现了端到端(end-to-end)的连接,你就可以检查你离目标还有多远,并在必要的情况下进行调整。一旦你完全瞄准,增加功能将是一件容易的事情。 

16、为了学习而制作原型 
Prototype to Learn 
任何带有风险的事物。以前没有试过的事物,或是对于最终系统极其关键的事物。任何未被证明的、试验性的、或有疑问的事物。任何让你觉得不舒服的东西。都可以通过制作原型来研究。比如:架构;已有系统中的新功能;外部数据的结构或内容;第三方工具或组件;性能问题;用户界面设计等等。 
原型制作是一种学习经验,其价值并不在于所产生的代码,而在于所学到的经验教训。 

17、靠近问题领域编程 
Program Close to The Problem domain 
计算机语言会影响你思考问题的方式,以及你看待交流的方式。用你的用户的语言进行设计和编码。 

18、估算,以避免发生意外 
Estimate to Avoid Surprises 
在着手之前先进行估算。你将提前发现潜在的问题。 
1)要选择能反映你想要传达的精确度的单位; 
2)基本的估算诀窍:去问已经做过这件事情的人; 
3)理解提问内容; 
4)根据对问题的理解,建立粗略、就绪的思维模型骨架; 
5)把模型分解为组件,找出描述这些组件怎样交互的数学规则,确定每个组件的参数; 
6)给每个参数指定值,找出哪些参数对结果的影响最大,并致力于让它们大致正确; 
7)进行多次计算,改变关键参数的值,然后根据那些参数表达你的答案; 
8)在被要求进行估算时说的话:“我等会回答你”。 

19、通过代码对进度表进行迭代 
Iterate the Schedule with the Code 
实行增量开发。追踪你的估算能力,提炼对迭代次数、以及在每次迭代中可以包含的内容的猜想。提炼会变得一次比一次好,对进度表的信心也将随之增长。你将给予管理部门你所能给予的最精确的进度估算。 

20、用纯文本保存知识 
Keep Knowledge in Plain Text 

保证不过时; 
杠杆作用:每一样工具,都能够在纯文本上进行操作; 
更易于测试; 
你需要确保所有各方能够使用公共标准进行通信。纯文本就是那个标准。 
 

21、利用命令shell的力量 
Use the Power of Command Shells 
GUI环境通常受限于它们的设计者想要提供的能力。当你想要快速地组合一些命令,以完成一次查询或某种其他的任务时,命令行要更为适宜。多使用你的命令shell,你会惊讶它能使你的生产率得到怎样的提高。 

22、用好一种编辑器 
Use a Single Editor Well 
选一种编辑器,彻底了解它,并将其用于所有的编辑任务。如果你用一种编辑器进行所有的文本编辑活动,你就不必停下来思考怎样完成文本操纵:必需的键击将成为本能反应。编辑器将成为你双手的延伸;键会在滑过文本和思想时歌唱起来。这就是我们的目标。 

23、总是使用源码控制 
Always Use Source Code Control 

总是。即使你的团队只有你一个人,你的项目只有一周时间;确保每样东西都处在源码控制之下。 
源码控制是你的工作的时间机器——你能够回到过去。 
把整个项目置于源码控制系统的保护之下具有一项很大的、隐蔽的好处:你可以进行自动的和可重复的产品构建。 
 

24、要修正问题,而不是发出指责 
Fix the Problem,Not the Blame 
要接受事实:调试就是解决问题,要据此发起进攻。Bug是你的过错还是别人的过错,并不是真的很有关系。它仍然是你的问题。 

25、不要恐慌 
Don't Panic 
做一次深呼吸,思考什么可能是bug的原因。 

要总是设法找出问题的根源,而不只是问题的特定表现; 
搜集所有的相关数据; 
开始修正bug的最佳途径是让其可再现; 
使你的数据可视化; 
跟踪:观察程序或数据结构随时间变化的状态; 
找到问题的原因的一种非常简单、却又特别有用的技术是向别人解释它。你只是一步步解释代码要做什么,常常就能让问题从屏幕上跳出来,宣布自己的存在。 
 

26、“Select”没有问题 
"Select" Isn't Broken 
Bug有可能存在于OS、编译器、或是第三方产品中——但这不应该是你的第一想法。有大得多的可能性的是,bug存在于正在开发的应用代码中。与假定库本身出了问题相比,假定应用代码对库的调用不正确通常更有好处。即使问题确实应归于第三方,在提交bug报告之前,你也必须先消除你的代码中的bug。 

27、不要假定,要证明 
Don't Assume it - Prove It 
不要因为你“知道”它能工作而轻易放过与bug有牵连的例程或代码。证明它。在实际环境中——使用真正的数据和边界条件——证明你的假定。 

28、学习一种文本操作语言 
Learn a Text Manipulation Language 
你用每天的很大一部分时间处理文本,为什么不让计算机替你完成部分工作呢? 
应用示例: 

数据库schema维护; 
Java、C#属性(Property)访问; 
测试数据生成。 
 

29、编写能编写代码的代码 
Write Code That Writes Code 
代码生成器能提高你的生产率,并有助于避免重复。 

30、你不可能写出完美的软件 
You Can't Write Perfect Software 
这刺痛了你?不应该。把它视为生活的公理,接受它,拥抱它,庆祝它。因为完美的软件不存在。在计算机简短的历史中,没有一个人曾经写出过一个完美的软件。你也不大可能成为第一个。除非你把这作为事实接受下来,否则你最终会把时间和精力浪费在追逐不可能实现的梦想上。 

31、通过合约进行设计 
Design with Contracts 
什么是正确的程序?不多不少,做它声明要做的事情的程序。用文档记载这样的声明,并进行校验,是按合约设计(简称DBC)的核心所在。 
这里,强调的重点是在“懒惰”的代码上:对在开始之前接受的东西要严格,而允诺返回的东西要尽可能少。 
使用DBC的最大好处也许是它迫使需求与保证的问题走到前台来。在设计时简单地列举输入域的范围是什么、边界条件是什么、例程允诺交付什么——或者,更重要的,它不允诺交付什么——是向着编写更好的软件的一次飞跃。不对这些事项作出陈述,你就回到了靠巧合编程,那是许多项目开始、结束、失败的地方。 

32、早崩溃 
Crash Early 
死程序不说谎。 
当你的代码发现,某件被认为不可能发生的事情已经发生时,你的程序就不再有存活能力。从此时开始,它所做的任何事情都会变得可疑,所以要尽快终止它。死程序带来的危害通常比有问题的程序要小得多。 

33、如果它不可能发生,用断言确保它不会发生 
If It Can't Happen,Use Assertions to Ensure That It Won't 
断言验证你的各种假定。在一个不确定的世界里,用断言保护你的代码。 
不要用断言代替真正的错误处理。断言检查的是决不应该发生的事情。 

34、将异常用于异常的问题 
Use Exceptions for Exceptional Problems 
异常表示即使的、非局部的控制转移——这是一种级联的(cascading)goto。异常应保留给意外事件。那些把异常用作其正常处理的一部分的程序,将遭受所有可读性和可维护性问题的折磨。这些程序破坏了封装:通过异常处理,例程和它们的调用者被更紧密地耦合在一起。 

35、要有始有终 
Finish What You Start 
只要可能,分配某资源的例程或对象也应该负责解除其分配。 

36、使模块之间的耦合减至最少 
Minimize Coupling Between Modules 

编写“羞怯”的代码; 
函数的得墨忒耳(Demeter)法则规定,某个对象的任何方法都应该只调用属于以下情形的方法: 
1)它自身; 
2)传入该方法的任何参数; 
3)它创建的任何对象; 
4)任何直接持有的组件对象。 
物理解耦。 
 

37、要配置,不要集成 
Configure,Don't Integrate 
细节会弄乱我们整洁的代码——特别是如果它们经常变化。把它们赶出代码。当我们在与它作斗争时,我们可以让我们的代码变得高度可配置和“软和”——也就是,容易适应变化。 
要用元数据(metadata)描述应用的配置选项:调谐参数、用户偏好(user preference)、安装目录,等等。 

38、将抽象放进代码,细节放进元数据 
Put Abstractions in Code,Details in Metadata 
但我们不只是想把元数据用于简单的偏好。我们想要尽可能多地通过元数据配置和驱动应用。我们的目标是以声明方式思考(规定要做什么,而不是怎么做),并创建高度灵活和可适应的应用。我们通过采用一条一般准则来做到这一点:为一般情况编写程序,把具体情况放在别处——在编译的代码库之外。 
也许你在编写一个具有可怕的工作流需求的系统。动作会根据复杂的(和变化的)商业规则启动和停止。考虑在某种基于规则的系统(即专家系统)中对它们进行编码,并嵌入到你的应用中。这样,你将通过编写规则、而不是修改代码来配置它。 

39、分析工作流,以改善并发性 
Analyze Workflow to Improve Concurrency 
时间是软件架构的一个常常被忽视的方面。时间有两个方面对我们很重要:并发(事情在同一时间发生)和次序(事情在时间中的相对位置)。 
我们在编写程序时,通常并没有把这两个方面放在心上。当人们最初坐下来开始设计架构,或是编写代码时,事情往往是线性的。那是大多数人的思考方式——总是先做这个,然后再做那个。但这样思考会带来时间耦合:方法A必须总是在方法B之前调用;同时只能运行一个报告;在接收到按钮点击之前,你必须等待屏幕重画。“嘀”必须在“嗒”之前发生。 
这样的方法不那么灵活,也不那么符合实际。 
我们需要容许并发,并考虑解除任何时间或者次序上的依赖。 

40、用服务进行设计 
Design Using Services 
实际上我们创建的并不是组件,而是服务——位于定义良好的、一致的接口之后的独立、并发的对象。 
通过把你的系统架构成多个独立的服务,你可以让配置成为动态的。 

41、总是为并发进行设计 
Always Design for Concurrency 
首先,必须对任何全局或静态变量加以保护,使其免于并发访问,现在也许是问问你自己、你最初为何需要全局变量的好时候。此外,不管调用的次序是什么,你都需要确保你给出的是一致的状态信息。 
在被调用时,对象必须总是处在有效的状态中,而且它们可能会在最尴尬的时候被调用。你必须确保,在任何可能被调用的时刻,对象都处在有效的状态中。这一问题常常出现在构造器与初始化例程分开定义的类中(构造器没有使对象进入已初始化状态)。 
一旦你设计了具有并发要素的架构,你可以灵活地处理应用的部署方式:单机、客户-服务器、或是n层。 

42、使视图与模型分离 
Separate Views from Models 
也就是常说的MVC模式(Model-View-Controller)。 

模型。表示目标对象的抽象数据模型。模型对任何视图或控制器都没有直接的了解。 
视图。解释模型的方式。它订阅模型中的变化和来自控制器的逻辑事件。 
控制器。控制视图、并向模型提供新数据的途径。 
通过松解模型与视图/控制器之间的耦合,你用低廉的代价为自己换来了许多灵活性。 
 

43、用黑板协调工作流 
Use Blackboards to Coordinate Workflow 
用黑板协调完全不同的事实和因素,同时又使各参与方保持独立和隔离。 
现代的分布式类黑板(blackboard-like)系统,比如JavaSpaces和T Spaces。 

44、不要靠巧合编程 
Don't Program by Coincidence 

总是意识到你在做什么。 
不要盲目地编程。试图构建你不完全理解的应用,或是使用你不熟悉的技术,就是希望自己被巧合误导。 
按照计划行事。 
依靠可靠的事物。如果你无法说出各种特定情形的区别,就假定是最坏的。 
为你的假定建立文档。“按合约编程”有助于澄清你头脑中的假定,并且有助于把它们传达给别人。 
不要只是测试你的代码,还要测试你的假定。 
为你的工作划分优先级。 
不要做历史的奴隶。不要让已有的代码支配将来的代码。 
所以下次有什么东西看起来能工作,而你却不知道为什么,要确定它不是巧合。 
 

45、估算你的算法的阶 
Estimate the Order of Your Algorithms 
在你编写代码之前,先大致估算事情需要多长时间。 

46、测试你的估算 
Test Your Estimates 
对算法的数学分析并不会告诉你每一件事情。在你的代码的目标环境中测定它的速度。 

47、早重构,常重构 
Refactor Early,Refactor Often 
在需要时对代码进行重写、重做和重新架构。要铲除问题的根源。不要容忍破窗户。 
关于重构,详见Martin Fowler的《重构》一书。 

48、为测试而设计 
Design to Test 
在你还没有编写代码时就开始思考测试问题。测试驱动开发? 

49、测试你的软件,否则你的用户就得测试 
Test Your Software,or Your Users Will 
测试是技术,但更是文化。一点预先的准备可以大大降低维护费用、减少客户服务电话。 

50、不要使用你不理解的向导代码 
Don't Use Wizard Code You Don't Understand 
向导很了不起。只需要点击一个按钮,回答一些简单的问题,向导就会自动为你生成骨架代码(skeleton code)。但如果你使用向导,却不理解它制作出的所有代码,你就无法控制你自己的应用。你没有能力维护它,而且在调试时会遇到很大的困难。 

51、不要搜集需求——挖掘它们 
Don't Gather Requirements - Dig for Them 
需求很少存在于表面上。它们深深地埋藏在层层假定、误解和政治手段的下面。 

52、与用户一同工作,以像用户一样思考 
Work with a User to Think Like a User 
要了解系统实际上将如何被使用,这是最好的方法。开采需求的过程也是开始与用户群建立和谐的关系、了解他们对你正在构建的系统的期许和希望的时候。 

53、抽象比细节活得更长久 
Abstractions Live Longer than Details 
“投资”于抽象,而不是实现。抽象能在来自不同的实现和新技术的变化的“攻击”之下存活下去。 

54、使用项目词汇表 
Use a Project Glossary 
如果用户和开发者用不同的名称指称同一事物,或是更糟,用同一名称指称不同事物,这样的项目很难取得成功。 

55、不要在盒子外面思考——要找到盒子 
Don't Think Outside the Box - Find the Box 
在遇到不可能解决的问题时,问问自己以下问题: 

有更容易的方法吗? 
你是在设法解决真正的问题,还是被外围的技术问题转移了注意力? 
这件事情为什么是一个问题? 
是什么使它如此难以解决? 
它必须以这种方式完成吗? 
它真的必须完成吗? 
很多时候,当你设法回答这些问题时,你会有让自己吃惊的发现。很多时候,对需求的重新诠释能让整个问题全部消失。 
你所需要的只是真正的约束、令人误解的约束、还有区分它们的智慧。 
 

56、倾听反复出现的疑虑——等你准备好再开始 
Listen to Nagging Doubts - Start When You're Ready 
你的一生都在积累经验与智慧。当你面对一件任务时,如果你反复感觉到疑虑,或是体验到某种勉强,要注意它。你可能无法准确地指出问题所在,但给它时间,你的疑虑很可能就会结晶成某种更坚实的东西,某种你可以处理的东西。软件开发仍然不是科学。让你的直觉为你的表演做出贡献。 

57、对有些事情“做”胜于“描述” 
Some Things Are Better Done Than Described 
你应该倾向于把需求搜集、设计、以及实现视为同一个过程——交付高质量的系统——的不同方面。不要掉进规范的螺旋,在某个时刻,你需要开始编码。 

58、不要做形式方法的奴隶 
Don't Be a Slave to Formal Methods 
如果你没有把某项技术放进你的开发实践和能力的语境中,不要盲目地采用它。 

59、昂贵的工具不一定能制作出更好的设计 
Expensive Tools Do Not Produce Better Designs 
小心供应商的炒作、行业教条、以及价格标签的诱惑。在考察工具的产出时,试着不要考虑它值多少钱。 

60、围绕功能、而不是工作职务进行组织 
Organize Around Functionality,Not Job Functions 
把你的人划分成小团队,分别负责最终系统的特定方面的功能。让团队按照个人的能力,在内部自行进行组织。 
但是,只有在项目拥有负责的开发者、以及强有力的项目管理时,这种途径才有效。创立一组自行其是的团队并放任自流,是一种灾难性的处方。 
要记住,团队是由个体组成的。让每个成员都能以他们自己的方式闪亮。 

61、不要使用手工流程 
Don't Use Manual Procedures 
shell脚本或批处理文件会一次次地以同一顺序执行同样的指令。我们可以自动安排备份、夜间构建、网站维护、以及其他任何可以无人照管地完成的事情。让计算机去做重复、庸常的事情——它会做得比我们更好。我们有更重要、更困难的事情要做。 

62、早测试,常测试,自动测试。 
Test Early.Test Often.Test Automatically. 
与呆在书架上的测试计划相比,每次构建时运行的测试要有效得多。 

63、要等到通过全部测试,编码才算完成 
Coding Ain't Done 'Til All the Tests Run 
就是这样。 

64、通过“蓄意破坏”测试你的测试 
Use Saboteurs to Test Your Testing 
在单独的软件副本上故意引人bug,以检验测试能够抓住它们。 

65、测试状态覆盖,而不是代码覆盖 
Test State Coverage,Not Code Coverage 
确定并测试重要的程序状态。只是测试代码行是不够的。即时具有良好的代码覆盖,你用于测试的数据仍然会有巨大的影响,而且,更为重要的是,你遍历代码的次序的影响可能是最大的。 

66、一个bug只抓一次 
Find Bugs Once 
一旦测试员找到一个bug,这应该是测试员最后一次找到它。此后自动测试应该对其进行检查。 

67、把英语当作又一种编程语言 
Treat English as Just Another Programming Language 
像你编写代码一样编写文档:遵守DRY原则、使用元数据、MVC、自动生成,等等。 

68、把文档建在里面,不要拴在外面 
Build Documentation In,Don't Bolt It On 
与代码分离的文档不太可能被修正和更新。使用像JavaDoc和NDoc这样的工具,我们可以根据源码生成API级的文档。 
文档和代码是同一底层模型的不同视图,但视图是唯一应该不同的东西。 

69、温和地超出用户的期望 
Gently Exceed Your Users' Expectations 
要设法让你的用户惊讶。请注意,不是惊吓他们,而是要让他们高兴。要理解用户的期望,然后给他们的东西要多那么一点。给系统增加某种面向用户的特性所需的一点额外努力将一次又一次在商誉上带来回报。 

70、在你的作品上签名 
Sign Your Work 
我们想要看到对所有权的自豪。“这是我编写的,我对自己的工作负责。”你的签名应该被视为质量的保证。当人们在一段代码上看到你的名字时,应该期望它是可靠的、用心编写的、测试过的和有文档的,一个真正的专业作品,由真正的专业人员编写。 

2009年3月19日星期四

完美是不存在的

完美是不存在的,Bug是一定存在的。

Chrome的一排标签页不知道怎么被我捣鼓面目全非,跟背后的标题栏争相显示。
QQ登录后没有一个好友显示在线,这怎么可能呢?离线再上线,在线人数一堆。

软件要么做得足够简单,明显得没有Bug;要么做得足够复杂,没有明显的Bug。

2009年2月15日星期日

慢慢用心体会

生活需要慢慢用心体会,我总是不喜欢一年一度的说法,节日是一年一度的,可是每一天都是一年一度的啊,虽然不是每一天都是节日,可是每一天都要用心去体会,生活是在每一个清晨,每一个傍晚,每一个瞬间,而不只是每一个节日。

某天早晨醒来,拿起魔方转来转去,突然体会到了F2L的妙处,生活就是这样。

2009年2月7日星期六

想吃涮羊肉

前几天小强打电话来的时候,我正和胡铁花在超市采购吃的,准备让他展示一下厨艺,俩人小酌几杯。小强问我什么去北京,他说我想吃涮肉的意思是想去北京。其实我真想去北京,但也真想吃涮肉。可是没找到什么去北京的借口,春节回去时吃涮肉也没什么味道了。
昨天跟同事出去吃饭,坐着车想来想去,也没商量出一个好的去处,最后想到了涮羊肉,又打车折回来去涮肉馆,怎么感觉就是没有北京的好吃。难怪在QQ签名上一写出想吃涮羊肉,北京的朋友看见了就回一句“来北京,我请你涮肉”,小强又说他家门口开了家超棒的涮肉馆来诱惑我。可是,终究在北京的一整天,我连门都没出。