RSS 2.0 Feed
2004-09 Entries
摘要:最近在时间的细屑里面挤出病毒般大小的时间来看GF调不出来的程序,简直是郁闷死人了。又要看好多年没有真正用过的C++,真是一种头痛欲裂的感觉,在一次感受到C++的疯狂之处……(这个程序其实之前确实是在用C++写的,后来因为某种原因,放弃了C++当中的++部分,用了几乎纯粹C的功能来编写。准确一点讲,标题应该是“这年头,还有人用C的吗?”)大家先来看两张对比的图,着一张是正确的输出结果:再来看一张错误的输出结果:(这一个是我需要调的程序)然后就开始要求我GF赶紧对公式,对比正确输出所用程序的代码,看看两者到底有什么地方不一样。由于两个程序所用的坐标系不一样,所以对比起来比较困难,最后费了好大的劲,把坐标系修改成完全一样的之后,发现问题依旧。这个时候另外一个现象更加困惑了我:在修改错误程序的坐标系之后,发现图形的缩小程度明显改善了,但是仍然是严重收敛的。这个现象着实打击了我一下,因为我一开始只是以为是误差量的积累造成的。难道说就是调整了一下几个公式的位置以及一些正负号就会造成误差累积量的不同?还真是前所未见的情况!这里整个调试过程是非常痛苦的,因为总共大约有几千万次的循环,计算量之大不可想象。而输出正确和错误的程序之间,在大约每60次循环才会产生大约1x10-7细微差别。难为我GF在那里单步调试了几十万次的循环,真是可怜啊。到底是哪里出了问题呢?多亏我用VC7来运行了一遍,才发现问题所在。(之前都是我在抽空Review代码,GF在1000km+的距离以外用VC6在调试。)下面这段代码就是存在错误的地方: void MultiMatr(int s, int n, int t, double *arr1, double *arr2, double *result){    double *tmp;    for (int i = 0; i < s; i++)    {        for (int j = 0; j < t; j++)        {            tmp = result + t * i + j;            *tmp = 0;            for (int k = 0; k < n; k++)            {                *tmp += ((*(arr1 + n * i + k)) * (*(arr2 + t * k + j)));             }        }    }}/**//***************************** update ***************************/void UpdateQ(double WnbbA[3]){    int        i;    double    deltaSita0, angSin, angCos;    double    deltaSita[3], qChange[3], qTrans[4][4];    deltaSita0 = 0;        for (i = 0; i < 3; i++)    {        deltaSita[i] = WnbbA[i] * quaDel;        deltaSita0 += deltaSita[i] * deltaSita[i];    }    deltaSita0 = sqrt(deltaSita0);    angCos = cos(deltaSita0 / 2);    if (deltaSita0 == 0)        angSin = 0.5;    else        angSin = sin(deltaSita0 / 2.0)/deltaSita0;    qTrans[0][0] = qTrans[1][1] = qTrans[2][2] = qTrans[3][3] = angCos;    qTrans[0][1] = qTrans[3][2] = -(angSin * deltaSita[0]);    qTrans[1][0] = qTrans[2][3] = -qTrans[0][1];    qTrans[0][2] = qTrans[1][3] = -(angSin * deltaSita[1]);    qTrans[2][0] = qTrans[3][1] = -qTrans[0][2];    qTrans[0][3] = qTrans[2][1] = -(angSin * deltaSita[2]);    qTrans[3][0] = qTrans[1][2] = -qTrans[0][3];    MultiMatr(4, 4, 1, (double*)qTrans, (double*)q, (double*)qChange);    for (i = 0; i < 4; i++)        q[i] = qChange[i];}大家看出来那里错了吗?真是稀松平常的错误啊!害死人了。...[阅读全文]

posted @ | Feedback (28) | Filed Under [ 其他 ]

摘要:经过多天的苦等,开发样机终于到了。其实是一个比较“旧”的型号了,dopodo 535。赶紧拿机子来玩玩,blog都不看了(先写一篇,立此存照,证明我比JGTM'2004要造一步接到手机!)大家看我工作卖力气吧!JGTM'2004还在睡觉呢!:-D(天晓得JGTM'2004今天早上几点钟睡的觉,这两天我到亲戚家去了……休假休假:-)   )...[阅读全文]

posted @ | Feedback (10) | Filed Under [ 其他 ]

摘要:最近在看两本书,一本是敏捷软件开发,另外一本是微软团队的成功之路。两本书都是还没有看完一半,但是都有了不少的体会,觉得自己的思路开阔了不少。以前总以为做架构师,或者做项目经理一类型的职业,都需要有非常深入的技术知识。后来发现不是这么一回事,这种高级位置上面的职业需要有一种总体的把握能力。正确的讲,并不是不需要深厚的技术知识作为基础,相反,技术知识非常重要,但是处于不同的层面,就有不同的东西需要关注。在架构层面,或者项目层面上,需要关注的实际上并不是技术,而是诸如组织团队、分配资源、管理项目等等。在这两本书里面我们都可以或多或少的看到背后所关注的“人”的问题,怎么样让人发挥最大的创造力,使团队成功的关键之一。当然大家的创造力必须统一到一个大方向上面,这就是领导者的职责所在。有时候我们会有这么一些经历:上司不赋予你和你的团队以明确的权利和责任,整个项目不关注与用户的沟通,项目进度和想象当中的差别很大,以及团队内部不良情绪在蔓延。如果你和你领导的团队没有明确的权利与责任,那就比较容易做事缩手缩脚,对于一些明显会出问题的地方没有人愿意指出或者不知道该向谁指出,当真正出现问题的时候没有人能够承担,即使有人承担了,那也会有一种替罪羊的感觉。在这么一种环境里面,每个人都会觉得什么时候倒霉的事情就会落到我的头上了。如果拥有了明确的权利和责任,还需要用合适的方式去分配。其实自己最清楚自己的实力,如果让大家坐下来进行商讨,也许会得到更满意的结果。每一个人能做什么不能做什么,不应该决定于他所在的位置或者在公司呆得时间的长短,而应该是专业方面的知识水平。如果你把一些其实你没有能力控制好的事情把握在自己的手上,最后有可能倒霉的是自己。项目最终是否成功,是看你对设计是否满意呢,还是看用户对需求的实现是否满意?明显是后者。你的产品赚不赚钱,看你的用户满不满意,用户则只关心他想要的东西你有没有给他,至于里面的技术细节则是一点都没有兴趣关心,因此开发的时候一定要立足于满足需求这一点。然而过去的软件工程方法,在这方面我看是有缺陷的。里面虽然有需求分析,但是这里的分析一共只进行一次,并且期待着尽量在整个开发阶段封闭需求。之前我的一个Post也说到了,除了需求随着时代的变化会变化之外,客户看到了一个发布之后就可能对以前的需求有了新的了解,也会产生变化。从这个方面来说,封闭需求就是不明智的一件事情。然而封闭需求还会导致一件非常不好的事情,它会让我们轻易的陷入从一开始就把各个细节设计清楚。事实证明,很多时候不要说客户,连我们这些开发人员对于整个东西是什么,而我们却总是对自己的思考能力过于高估,而对想象能力过于低估。最后我们想出来很多很多的功能,但是有好一部分却不不符合需求,此外还会有很多没有用的东西出现。开发的过程需要尽快发布新的原型,让用户去进行实际体验,看看有什么不符合的。其实传统的软件工程学里面也说到,项目越是进行到后面的阶段,修改编码尤其是修改需求变化方面的代码代价就越大,这一句话确实是对的。然而过去的很多方法比如瀑布模型,却企图把需求封闭在一个特定的阶段(需求分析阶段,顶多是到设计阶段),一旦到了编码就不允许对需求甚至设计进行修改。这是一个愚蠢的方法,除非你确实是这一个方面的专家,有着丰富的经验,并且引领着行业的潮流,你说了就算。不幸的是很多时候不是你说了算,而是客户说了算,就算你说你的设计多名优秀,客户说不要那就是不要。封闭需求、设计之后,一旦用户对最终产品不满意,那么实际上还是需要回到需求分析阶段,这个时候代价就非常的大了。所谓的敏捷开发的其中一个目的就是希望让需求变化来的越早越好,减少在开发后期才出现的需求变化。不停地进行迭代,尽量缩短每次发布的周期。在每一次迭代完成之后,让用户去亲身感受一下,可以让你知道这一阶段的设计是否就是客户想要的东西。因此一个项目要成功,和用户进行沟通实际上是很重要的,这种沟通越频密越好。有人就会说了,我的用户在十万八千里以外,不可能经常来我们这里进行体验。如果说请求对方指派代表是不可行的,那么也需要考虑在项目团队内部设立一个专门进行模拟客户的角色,这个人必须熟悉这一个市场。如果没有人熟悉,那么就专门找一个人来熟悉这方面的工作。这一个角色总得由人来担任,就像编码人这个角色总得由人来担任一样。如果我们能够真正的从客户这个角度来考虑问题,我们就会发现我们以前的很多设计方法是有问题的。比如说我们会产生一大堆的文档,从细小到如何对数据进行排序的函数开始设计代码,在设计里面为以后可能出现的需求变化留下“钩子”。为什么说这样做有问题?我们首先看看文档,难道用户需要的是文档吗?需要看看你的FooStory是派生还是引用JokeImplement的设计文档吗?还是TempleteMethod/MVC/Decorate之类的词语在某个文档中出现会让他升官发财?统统都不是!他要的是能够正确运行并且提供他所需要的功能的软件。软件才是中心,不是吗?有人也许会说,UML之类的东西是为了让我们的设计更加清晰。可是这些UML图最终还是要转化为代码的,如果你的代码设计的充分好,就会有比较好的自解释能力,就算什么地方有不太容易理解的地方,在代码里面添加简单的注释基本上都能够解决问题。反过来说,当你的程序因为Bug或者其他原因需要进行调试和修改,你觉得你首先要读的是UML图还是代码呢?代码永远都只有一种解释,但是文档则难免会有二义性存在。思考完这些问题,我得出一个结论:我们应该围着代码转,然后产生文档,而不是围着文档转,然后产生代码。接下来看看设计的方向。因为我们错误的从一开始就把整个需求确定下来,甚至到了毛细血管的程度,所以我们会不自觉地首先实现最为底层的东西。这个惯性很好解释,因为我们需要编译通过。如果class a里面使用了class b的实例,如果首先写class a,肯定无法立即编译通过。如果我们要一次过完成一个可编译的版本,那么从上面开始写,三天三夜不眠不休都不可能完成。所以我们就从class b开始我们的工作。但是这样却需要等你将整个系统的所有部分(至少是主要部分)都完成了之后,才可能发布一个可以给用户演示的版本,需要的时间通常会非常漫长。如果我们反过来从顶层开始往下写,只要我们设法保证每天都能够产生一个可用版本,那么随时都可以拿出来给用户演示。实际上一些很细节的功能用户一开始并不会很清楚,所以我们也不可能很清楚,一些东西也许并不会出现,另外一些东西也许没有考虑进去,还有一些则并不是你原来想象的那样。如果一开始就埋头做哪些细节的功能,很可能最后会发现,有很多东西其实就是在浪费你的青春。至于怎么进行自上往下的设计,留着以后再讲。另一个我们比较容易犯的错误,就是留“钩子”。这个错误我也一直在犯,并且总是忍不住会犯。很简单,你的经验告诉你,需求是会变化的,因此需要考虑为以后的需求变化预先进行设计。这是一个非常愚蠢的想法,当我明白这一点之后我简直是无地自容。前面我们已经看到了,在整个开发的过程中,需求就会不断地变化。这种变化就像一串随机序列,根本就无法把握。你以为它会是那样的,甚至可能客户一开始也那样以为,接下来没几天客户就说不是那样的。如果你能够在一开始就能预测这种变化的方向,你就有能力应用瀑布模型,你就可以封闭需求,你就可以简单的面对客户今天说要这个明天说不要那个的情况。事实上明显不是这样的,需求的更改不可能按照你的设想去进行的。如果说今天你要设计的软件的需求,你都不可能预见,那么你凭什么那么自信的认为你知道明天的需求要变成什么?“即使最后发现需求确实不是向着你想的方向发展,那这些代码顶多是没有用,难道还会有害么?”我认为是有害的,因为它影响了你的重构。很多时候我们解决某一类特定的问题可能会有好多的方法,比如我们可以Templete或者Strategy等等,每一种解决方法都有它的侧重点和副作用。假设我们把Templete用在了这些地方,到最后也许我们的需求变化要求我们使用Strategy,那么要从整个设计里面拔除已有的Templete相关的代码将会是痛苦的。实际上问题可能还会更严重,假如你这个Templete是在一个组件里面,拔除这个Templete很可能会对另外一个使用这个组建的系统产生影响,最后你不得不修改另外几个系统,重新deploy。当然,这个也许是极端的情况,但无论如何它肯定会对你未来的修改或者重构造成障碍。好了,今天只是一个粗略的笔记,详细的内容日后再写,来日方长嘛。本来还想画一些图,让页面更加rich一点,但是已经霸占JGTM的19'一天了,不好意思霸占下去。回到另外一台17'的显示器,就完全没有了画图的激情与欲望了。还是看看书算了……...[阅读全文]

posted @ | Feedback (23) | Filed Under [ Design & Architecture ]

摘要:这个问题我想也许有人的答案都跟我以前的答案差不多,不就是解决实际编码当中所遇到的问题吗?不就是解决怎么设计一个程序的问题吗?确实是这样,但这只是表面现象。如果没有深入的挖掘问题的本质,就可能会错误的是用这些知识。最近在JGTM2004这里工作的时间,我学到了不少东西,有了更高的觉悟,对这个问题的理解就是其中一方面。以前我就没有仔细去想过这个问题,并且站在一个实际做开发的人员的角度来看,自认为这个东西就是为了减轻开发人员的负担,并且为最终产品提供更加优良的特性。其实这个只是一方面,并且就我目前的理解觉得可能还是一个次要的方面。为了阐述这个问题,我不妨举一个具体的例子:某天你接到一个项目,客户说,我们需要一个对树型结构的数据节点进行遍历,同时对这里面的数据进行一个统计,你就开始噼里啪啦的敲键盘编码了。但是过两天客户跟你说了,现在要添加一个新的统计,于是你就开始辛辛苦苦的进行修改。再过两天用户又说了,去掉原来那一个,只要后来增加的部分。等待产品快要发布了,用户一看,说:“哎呀,看来这样还是不好,你还是给我显示原来的统计数据吧。”等到展品发布之后过了一个月,用户打电话找你然你进行维护——当初没有说清楚,实际上要遍历的数据是网络型结构的…… 现实确实就是这样的,事情总是在变。唯一不变的就是一直在变,这个确实是一句名言,甚至就是真理。我想大家也一定认同,问题是作为普通的程序员,可能没有意识到这个变化是从一开始进行开发就出现的,并且在整个产品的生命周期里面,出变化最多的可能是产品发布之前的阶段。而产品一旦发布了,用户通常也能够理解有一些东西改变起来不方便。如果我们从管理高层的角度看问题,可能会更容易解释这个问题。实际上制约一个软件成功的最大要素并不在于技术,而在于两个制高点——“创意”和“市场”。创意我们不谈,我们就说这个市场。你的产品是否买得出去,在于你的产品是否能够适应并满足市场的需求,不能够满足需求的东西显然不可能赚钱。至于你的技术怎么样,用户根本就不关心,甚至你的产品写的很垃圾都好,只要你能够满足用户需求满足市场需求,那就一定赚大钱。那么怎么才能够尽最大的程度满足需求呢?在一个项目的最开始,我们肯定不会非常清楚。如果我们按照自己一知半解的假设去做,那么最终做出来的东西可能就不是用户所需要的,至少是用户所最需要的。所以我们需要用户给我们提出需求,到这里就是传统软件工程学里面的思想。很可惜的是,用户根本就不知道自己需要些什么,而我们对于用户想表达的是什么更是没有头绪。 因此我们只好不停的跟用户接触,不停的把我们现阶段的东西展现给用户,哪怕没有具体实现,哪怕背后的数据都是假的,也要给用户一个Visualize的东西出来看看,这样用户才有可能把需求说明白:噢,这个确实是这样的,那个我不需要,但是我需要的是什么,能不能这么改,这么操作我不习惯,我就要那样的……有了这个你才可能进行更进一步的设计。这里就已经出现了一定的需求变化了——由于原始需求的定义是粗糙的,没有经过验证的,而现在进行了一定的验证之后可能发现原来的想象可能有问题,需要修改。于此同时,你也需要谨记,客户可能会随时反复。只要你的产品没有正式发布,你就没有理由不去响应客户的需求变化。因此你做出来的东西应该能够随时地,方便得在某种程度上面进行修改。简而言之,在设计和开发阶段,需求的变化是最为剧烈的,这种变动的次数可能会很多。怎样有效率的适应这些变化,我觉得可能是设计模式所要解决的一个很重要的问题。我们来假设一下,某个客户一共会更改10次的需求,如果你每次接到需求变化之后第二天就能够把修改过的演示版本提交给用户评议,而我则每次都要花费10天的时间,我们两个人之间的差别将会是多大。事实上市场的变化速度是相当快的,可以说一年前的产品基本上就进入了淘汰序列,如果你还在卖一年以前普遍使用的技术,那就是一件危险的事情——这表明你跟不上时代的节奏,不可能拉到大量的订单。这说明现代IT行业的产品有一个非常重要的一点就是从“创意”到“市场”这两点之间花费的时间越短越好,也就是要求你的开发效率要高。如果你的设计灵活,能够有效地应付变化,那么你就在这一点上面成功了。当然,这只是其中的一个方面。今天关于这方面问题的总结就暂时到这里,以后还有什么想法再贴。   P.s.: 其实我到了北京已经有一个星期左右了,这段时间也不是没有发Post,主要都发在cnblogs上面了。因为我担心我的那些Post对于各位大侠来说简直就是班门弄斧,实在不好意思放在博客堂里面。此外要说一声,JGTM2004的私家机器好爽啊!19'的屏幕确实非同凡响,现在看17'的屏幕就觉得好像在看14'的屏幕一样。好工具就是能够激发人的灵感和激情……...[阅读全文]

posted @ | Feedback (20) | Filed Under [ Design & Architecture ]