飞's profile程序人生PhotosBlogListsMore Tools Help

Blog


    June 21

    FireFox 3以及其他

    FireFox 3出来了,下载试用了一下,发现不错,比前两个版本好很多,另外貌似全世界下载数目有1000W份,真是天文数字。。。

    为什么我觉得它好呢,因为,我用它竟然能上MSN的space了,天可怜见,我已经很长时间不能用IE登录这东西了,一上去就到处报错,导致只能看,不能回复,也不能发,每次都是通过live writer写上去的,而且是通过手机去回复的。。。但是似乎在xp系统上就是好的,可能是server2003的安全限制,不知道怎么搞,但总之有一个可以用的就好了。而且之前一直不能上的某欢的新浪博客,现在也可以上了,看了这个圡人半年来的一些事情,呵呵。

    哭啊,我的hotmail邮件已经有100多个没有收了,虽然说基本上都是垃圾邮件,但是每次上MSN的时候跳出来提示给我说有多少个未读邮件,都很不爽,为了这个,我还尝试用outlook收hotmail,发现是不可以的,然后昨天弄了一个live mail,这个也可以搞,还不错。

    比较不好的是,live mail跟live writer这两个东西在server2003上都不能安装,所以只好在xp的机上安装了之后,把安装好的文件拷过来用,当然这是可以用的,因为都是用.net做的唉,有framework就可以,而且.net程序的特性就是把配置写在文件,而不是去搞什么注册表,所以就可以拷了用。

    唔,说什么好呢,还是说说对web的看法吧。我仍然觉得,在浏览器里面做复杂的应用程序是不太好的事情,尽管能做得出来。首先是做的过程非常的烦,有可能你要考虑兼容各种浏览器,IE,FireFox,Safari,于是就很郁闷,简单的程序不会遇到这个问题,但是复杂一点的,比如代码的语法,比如可用的API,比如样式的处理,它就很不同,代码里面充满了if(是IE())怎样怎样,else if(是FF())怎样怎样,嗯,然后,假如是IE,是某个版本怎样怎样,不是某个版本又怎样怎样,如果浏览器有bug,除了等开发它的人出补丁包之外,就毫无办法。

    典型的就是以前微软一个补丁包把popup弄崩溃的事情,终于发现是它的问题,反馈过去,还是等了1个月才来补丁,而且中文版本的要3个月。假如这是客户端呢,客户端的程序通常稳定得惊人,而且API极其丰富,大不了出问题之后自己实现一个某功能。

    比如微软的live战略,如果我做,绝对不会在web上投入这么多精力,主要应该致力于服务器性能的改善。web上要做的事情是什么?发表,阅读,评论,仅此而已。复杂的配置功能,花哨的各种东西,相册、图片上传,都可以做在客户端应用程序中,对于一个长期用户,他会不愿意去下载这个客户端吗?而且这个客户端是使用.net framework做的,客观上也推动了这个东西的市场。当然运行这个东西需要windows系统,如何面对其他OS的用户?虽然有mono,但是估计没有多少人愿意去用的,那么扩展一下web的功能,简单的配置都提供,比如添加好友,改变blog布局等等,简单的图片上传等等,这就可以了,使用非windows系统的人,也不会有很多愿意在msn的space上写东西,有发表、阅读、评论、贴图的功能,也已经足够了。想要更复杂花哨的功能吗?sorry, no .net framework, no way. 不光是space,live mail也是一个道理。

    我觉得,在web大行其道的同时,是否应该多考虑考虑客户端。微软以前提出过smart client这个概念,但是反响不怎么强烈。试试想一下,很多时候,用户想要花哨的功能,OK,你在web上努力实现了,很不幸,代码非常多,点一个页面,噢,我网络不好,于是可以去喝一杯茶。对于我们这样的业务系统来说,营业员往往每天都需要面对同一个模块做反复操作,假设他是跨网的,常常会遇到很慢的情况,操作的人很郁闷,恨不得砸了鼠标,排队等待处理的人也很郁闷,恨不得帮他一起砸鼠标。

    假如有客户端呢?服务端只需要有对应的web service,他的客户端在初次使用的时候安装,然后每次操作的时候,所传输的只是业务数据,而不是大量的界面源码,即使操作不成功,还可以在本地建一个操作队列,等网络恢复的时候批量重做,如何?

    另外,web上的打印,文件导出,实时监控等等功能,实在不是那么令人舒服的,假设有客户端,这一切都不是问题。我从来不认为web能够更好地做这些功能,相比来说,web更适合做一次性的工作,比如用户自己登录网上营业厅去改个服务,那没问题,你不能因为这个也要求他去装一个客户端,但是如果你是内部的营业员,每天跟这东西打100次交道,装个客户端恐怕不怎么为难。

    因此我心目中完美的方式,是web端跟客户端兼有,web里面只有简单及必须操作,客户端里面包含所有功能,嗯,客户端的修改之后的更新发布是个问题,但是我觉得应该还是有简单办法来实现的。

    另外你最期待的msn space功能是什么?我觉得是日志导出到本机查看,我知道这个怎么搞,我自己似乎也能做出来,但是回复怎么导出来呢,困惑中

    July 08

    关于文档对象模型

    数据的组织其实是一件很有趣的事情。

    基本的数据结构,在各种书里面都有讲到,比如数组啊,堆栈啊,散列啊,集合啊,树啊,这些都很常用,并且由它们构成了很多接近实用的东西。我来说说我对文档对象模型的理解吧。

    文档对象模型,所谓的Document Object Model,提供的是一套完整的对于层次化数据的封装,它的内部实现还是基于树的,描述了整个文档的组织结构,提供了一系列的属性和方法用于访问特定元素。

    我不太清楚DOM的内部实现,想试图通过我的理解,用我希望的方式,从无到有地实现它。

    就拿一个XML来说吧,它的结构是树的形状,这是没有疑问的,那么数据的存储也应当是层次化的,每一个节点保持了自身的父节点的一个引用parentNode,同时对它的直接子节点保持着有序的引用childNodes,这应该是一个双向链表关系。于是,我们很容易地就把这个树构造出来了。

    现在我们来考虑一个问题,节点是有属性的,比如说name属性,也会有唯一的标识id,如果我想要给外界提供这样的方法,比如说getElementById,可以通过给定的id来获取对应节点的引用,或者说,我要提供getElementsByName,用过给定名字来获取满足条件的节点引用的一个集合,这一步要如何去做?

    很自然的一个想法是遍历树,遍历树的一个非常自然的想法又是递归,逐次去向下寻找,这个办法好不好呢?我觉得是不好的,因为在元素很多的情况下,递归层次多了之后,会导致栈溢出,而且这个效率不敢保证。我的想法,是在根节点的层次,另外保持一个引用集合,这个集合保持了所有节点的引用,查找的时候从这个集合去找。

    很显然这需要开辟额外的内存空间,但是查找的时候,同级查找效率必然要比递归高,属于典型的空间换时间方法,在应用软件的场合,内存开销一般不是首要考虑的,而是要节约CPU,所以这种做法是值得使用的。

    这个做法,也带来了一些麻烦,那就是维护整个树的时候,需要同步更新这个顶层节点集合。再从另外一个角度讲,如果要求对每个节点,都提供查询它所有子节点的操作,那是不是每一个节点都需要保持它的所有子节点的一个引用呢?

    这么一来,内存开销可就大了,用个例子算一下看看,假设有一个树,每个非叶子节点都有2个子节点,典型的1248结构,这种叫做什么?完全二叉树吗?我忘记了。记得的人提醒一下。

    如果每个节点都需要保持自己父节点和直接子节点的引用,那么总的引用的个数就是:2+2×3+4×3+8×1=28,如果需要除了保持自身父节点和直接子节点的引用,还需要保持所有子节点的引用,那总的引用的个数就是:28+14+2×6+4×2=62,如果把树的层次加大,那么上层节点所需要保持的引用个数就多得惊人了。

    假如我来设计这个东西,那我绝对不会在每一个节点上保存它所有子节点的引用。不但如此,设想一下如果保存了这些引用,那最底层的叶子节点发生变化之后,它的所有父系节点都必须跟着更新引用,当树的层次比较深,而且叶子节点操作频繁的时候,是相当恐怖的。既然这样,那就只在根节点上保存所有节点引用就可以了吧。

    好,解决了这个问题之后,来看下另外一个东西:一个节点如何访问到它的兄弟节点。很显然它已经不用保持兄弟节点的引用了,因为它有父节点的引用,从父节点查询到所有子节点,除去自身,就得到了兄弟节点的集合了,对吧。这里我有一个疑问,节点自身,是否需要存放它在父节点中的序号?

    这是什么意思呢,某一个节点,我要获取它的nextSibling,这一步如何做到?哦,就去查询到它父节点的子节点有序集合,然后挨个找,当发现自己之后,下一个就是nextSibling了,对吗?对确实很对,不过考虑一下如果,这个节点的兄弟节点非常非常多,比如说100000个,而它自身是第99999个,那是不是做这次循环会非常傻?假设节点上保存了序号,就是说它知道自己是父节点的子节点中的第99999个,index=99999了,那我是不是可以用parentNode.children[index+1]这种方式来直接获取nextSibling呢?

    这个我不知道了,应该也是需要权衡的。嗯,整个DOM的建立,其实并不复杂,关键的就是数据如何组织,很大程度上,是空间跟时间综合考虑之后进行的取舍。

    另外这个XPath也很有意思,它提供的是从上而下按照路径的访问,这就要求访问的时候对于文档的结构必须非常明了。

    i+++++i+++++i+++++i++技术人员与非技术人员分隔线++i+++++i+++++i+++++i

    现在开始用生动形象的语言复述一遍:还是拿军队做比方,假设整个DOM结构就是一个集团军的组织结构,很明显这个组织结构是树的形状的,最顶层是司令部,下面是军部、师部,一直到士兵。每个单位都是必然知道它从属于哪个单位的,又必然是知道它领导了哪几个单位的(假如有的话)。

    现在我考虑的问题是,我到集团军的司令部,向他们查询所有名字叫做“张三”的士兵,如果他们经常面临这样的查询,那最好的方法并不是联系各军部,叫他们把各自的叫做“张三”的士兵的情况上报,而如果这样,他们也是必然一级一级从下面汇总上来的,这个过程太慢了。那司令部就需要保持一份名单:本集团军所有人员名单。这样我可以直接在这里查到所有名字叫做“张三”的士兵,而不需要经过那个繁琐的过程。

    但是我怎么确保司令部的这个名单是确定无误的,没有出现士兵退伍了没有在这里注销,或者有新的叫做“张三”的士兵入伍了,却没有登记到这份名单上来呢?那就要建立严格的制度,底层每次有任何人员变动,都必须上报。

    我考虑的另外一个问题就是,既然集团军司令部有了自己所有下属人员名单,那么各军部,师部之类的,是否也需要拥有自己的所有下属人员名单呢?在实际情况中,这当然并无不可,但是考虑到在计算机中,所有信息的存储都是需要空间的,而且更新是一个很烦恼的事情,这样,每个士兵的情况变动,就必须从连排一直更新到司令部了,这太繁琐了,还是直接上报一下司令部备案算了。

    我刚才说到的另外一个问题就是,某个单位,是否需要知道自己是在上级单位中的序号,假设有一个8连,按照现在三三编制,它显然是3营第2个连队,但是如果不是三三编制,而是一个上级单位都可以有若干个下属单位呢,它是否需要每次去上级单位找一下当前的配属部队列表,然后数一下自己是第几个?另外一种方式就是,上级单位如果又加入了新的下级部队,它对现有的下级部队进行重新排序,然后把序号告诉他们,让他们自己去记住。这也是可以的。这样,我问某连长:你是你所在营的下一个连队是哪个?他去问他上级的时候,就不必把所有兄弟连队都查出来,然后一个一个数了,而是可以直接问:第三个连队是哪个连?(假设他知道自己是第二个连队的话)当然,如果从现实生活中来看,这样也是非常傻的,呵呵,比方而已。

    那么这个XPath相当于什么呢,就好比说,我跟司令讲,叫第2军第3师第4团的所有营长过来跟开会,然后他就直接按照这个路径去找人了,这当然要求我对整个编制结构非常熟悉才能做到。

    另外有一个问题,你问某士兵,你的编制序列是什么?他说,某军某师某团某营某连某排某班。那现在把他这个团及其下属人员都直接调走了,去别的师了,不告诉他的话,他是不会知道的。对人来讲当然很简单,召集全团开个会,通知一下这个事情就可以了,对程序来说,就挺麻烦的,得遍历刷新一次,所以子节点是不宜保存他自身从根节点开始的全路径的,动态查询比较适宜。

    总而言之,DOM树的效率,关键在两个地方的取舍,1是多保存数据,查询的时候可以减少时间,但是更新的时候会比较麻烦,效率也低,2是只保存必要数据,这样不容易出错,查询的时候要耗一些时间。两者是矛盾的,就看使用的时候更在乎什么了。

    June 28

    对软件工程的一些想法

    说说对软件工程的一些看法。我觉得软件这个东西,并非是每天写写代码这么简单的事情,它是一种产品,既然是产品,那就有一个生产的过程,通常这个过程就是需求调研、设计、开发、测试,事实上,由于软件需求的差异太大,导致内部的实现也千差万别,实现过程是非常繁琐的。
    从几十年以前,人们就想办法来改进,比如说提取公共函数,比如说通过常数配置,希望使得软件模块的复用程度变大,减少多次开发的工作量,效果还是比较明显的。最近我还是有另外一些感想,对软件开发过程,觉得有很大的改进余地。
    这个想法是从微软的Factory开始的,看到公司里面的开发方式,挺原始的,就是大家开着一个编辑器,写啊写啊,遇到常用的东西,到某处去拷一段来,改一下,然后继续,说实话我很反感这样干活,挺无聊的啊,做了一年之后,你发现自己这一年都在搞这个?难受吗?
    我始终有一个观念,觉得人的能力是不可靠的,什么意思呢,人这种动物,他非常感性,经常要受到各种状态的影响,比如说你心情好啊,情绪激动,你写代码,一不小心就写错了个东西了,可是当时没发现。或者你心情又不好了,干活很没效率,一个下午什么都没做出来,这怎么行呢。
    我觉得应该由机器来代替人做这类重复劳动,人在旁边看着就可以了,就像收麦子,你很熟练是吗?你跟收割机比比呢?是的,我不会收麦子,可是我开着收割机,哗啦啦,一个下午把所有事情都搞定了,然后我晚上不加班,回家发发呆,吃吃西瓜,研究研究三国,你说这日子是不是很好。当然这是理想状态了。
    嗯,这就涉及到一个问题,你是否值得花这个时间来做一个收割机,比如说你只有2分地,那你做这收割机就简直是高射炮打蚊子,不划算,但是你要是自己有10000亩地,或者说有20000个你这样的,每个人都有2分地,那倒不妨试试,看能否造出来一个收割机,这个时候造出来,应该就能够大幅提高效率了,到后来,说不定你就不种地,专门给别人造收割机。
    软件它也是一种被称为工程的东西,可是跟汽车、集成电路之类的行业比,它就没有流水线,没有大规模生产的工厂,人数再多的软件公司,它也就像个规模大一点的作坊,这是很不对的,我觉得。现在看来,国外的一些公司已经很重视自动化生产软件了,比如微软做的这些Factory,老外他就爱好这些,事情做完,回家休息,效率第一。我感觉中国人或者说东亚人有一个特点,有时候觉得把自己搞得辛苦一点,功劳就大,甚至做好事情没事了也要加加班,能留下好印象,这何苦呢,工作不是生活的全部,为什么不想办法解放自己的双手,解放自己的思维,既能完成工作又不用加班呢?
    唉,再说点沉重的话题,说不定这种思维方式也不适合中国,你搞出很自动化的工具,一个人可以做很多事情,很多人就失业了,就像你要是大规模推广收割机之类的,那中国那么多农民干啥去?

    December 17

    标记语言+脚本语言 VS 高级OO语言 (一)

        我不是高手,因为,在我看来,才写过四万行代码的人是无论如何不能称为高手的,尽管人的领悟能力有区别,但是,不亲自杀猪的人肯定没法自称是杀猪高手,庖丁解了十多年的牛才能熟练到那种程度。
        在我学习编程的几年里,写过的程序半数以上跟图形或者显示有关,最早的是在Turbo C3.0下用C++调用DOS的图形接口绘制统计图的,后来到Windows平台上有用C语言调用Win32 API的GDI函数绘图的(2003年的联机象棋程序,2004年的自动计算点列的分界线程序、简单字母手写识别程序等等),有用Java2D绘制简单图形的,有用Java Swing做各种界面的,有用C#调用GDI+绘图的,还有最多的情况就是使用HTML跟Javascript混合编程(包括VML,HTC等等各种古怪的东西)的。用过这些东西,所以渐渐会有一些感悟,我只说UI和图形方面的。
        目前做UI主要有两种编程方式,一是用标记语言构造元素及其包含关系,然后用脚本语言响应事件处理;二是完全使用Java,C#这样的高级OO语言做。前一种方式使用起来很简单,但是当处理很复杂事件的时候就显得力不从心,最常见的是浏览器中的编程,HTML+Script,还有Macromedia的Flex,以及微软号称要推广的XAML。后面一种方式符合传统面向对象编程的习惯,数据封装性好,但是代码写起来真的是繁琐,尤其是没有一个好的UI设计器的时候。C#好一点,因为Visual Studio的设计器太强悍了,但是相比之下,用Java的Swing干活就麻烦了很多,也许是我孤陋寡闻,不知道有好的设计器,总之用JBuilder做Swing界面,想做什么效果就做不出什么效果,Eclipse又没有看到做UI的插件,所以,sigh~~
        从代码量来说,标记语言显然要小,基于XML的结构使得它天生就很好地处理了包含关系。如果你在一个Panel上放一个Label,一个Button,标记语言可以这样写:

        <Panel>
          <Label Text="我是一个标签" />
          <Button Text="我是一个按钮" />
        </Panel>
     
        看,多么简洁,但是用高级OO语言,呵呵,试试:

        Panel panel = new Panel();
        Label label = new Label("我是一个标签");
        Button button = new Button("我是一个按钮");
        panel.appendChild(label);
        panel.appendChild(button);
     
        然后,如果要对每个元素设置属性,那差别就更大了,标记语言可以使用类似CSS这样的东西来规范显示,或者用XSL来统一转换,但是高级OO语言里面一般只好去狂写了,panel.setWidth(123);panel.setHeight(121);……这样搞的话,如果界面元素多一点,工作量简直大得惊人,所以说,微软希望推广XAML还是有道理的。
     
    (附图,以前写的根据文件中的点列自动求解分割线方程的程序运行结果,C语言调用Win32API,lccwin32中编译,代码约20K)
    October 26

    关于Word的对象模型

        在这一期的《MSDN开发精选》杂志里,有两篇文章对我的触动很大。一篇是《使用Windows Forms生成简单的绘图工具》,一篇是《为.NET应用程序实现一个类似Word的对象模型》。
        前面一篇,重点在绘图的控制上,有些细节值得借鉴,比如事件处理,但是多数地方我做得不比他差了,他想到的那些地方,我也大体上考虑过。后面一篇就不一样了,我从来没有想过要把手上这个实现成类似于Word的对象模型,但是看完这篇文章,我问自己:为什么不可以这样做呢?
        这样有多难做?我仔细考虑过,没有发现什么特别难的地方,或许我考虑得还不够。实在可惜,没有能在项目开工的时候看到这篇文章,也怪我笨,Word对象模型的结构我不是不知道,从Application到Document,不知道为什么就从来没有触动过这种想法。虽然文章讲的是使用C#语言在DotNet平台上实现这样一个模型,但是以我在浏览器脚本上6、7年的功力,完全模仿一个也并非不可能,这样也就能解决多个流程同时打开编辑、并且支持切换。
        哦,这次已经做到这个地步,没法再进行改动了。不管它,我继续研究,把这个东西完全把握,万一以后要重新开发版本的时候,我可以Apply一把,哼哼。
        不知道怎么搞的,每次东西做完,都觉得做得不满意,拿微软那些东西一比,我写的东西又土气,功能又少一些,实在是一件郁闷的事情,每次刚写完就想重新写。这几年,写了3W多行代码都没做出让自己特别满意、可以拿出去折服一堆人的东西,唉,继续继续,埋头修炼~~
    August 12

    人性化软件模块设计——人群跟蚁群的较量

        本文讨论目的是软件模块设计的“人性化”,并非软件工程的人性化,而是从技术角度着眼,意图说明一种设计思想。

        面向对象的一个基本原则是分而治之(Divide and Conquer),这种方法论提倡将程序模块化,各模块实现单独的功能,在统一的管理下协同工作,构成整个系统。

        在具体实施的时候,又有两种倾向:将功能高度集中于主控制模块;将功能下放到各部件。这两种做法都有很高的可行性,也分别有大量支持者。我觉得在一些程度上,后者更贴近人类的思维方式,更适合用人性化的观念来解释。

        将两种类型的程序对应到生物集群,第一种相当于一个蚁群,第二个相当于人群。蚁群的特点是,个体能够完成的事务非常有限,但是因为在一个非常强有力的统治者蚁后的控制下,它们能够协同工作,统一调度,完成不可想象的事件。人群的特点是,每个人都可以独立思考,能够理解别人的指令,并且根据这些指令做到力所能及的事情。作为人群的统治者,他的智慧不需要比其他人的高太多,只需要从宏观上来把握一些事情即可。

        从系统的实现来说,第一种方式难度很高。完成单个蚂蚁(小模块)的功能并不复杂,创建大量的蚂蚁也只不过是需要的时间多一点,但是,当开始设计蚁后(总控模块)的时候,噩梦开始了,整个调度算法实在是一件令人头疼的事情。对于比较复杂一点的系统,让一个人去设计这个模块简直是不可思议,但是如果由多个人共同完成这个模块,又面临着互相理解的问题,每个人的思路都不相同,在努力协作的过程中,大量的时间被浪费在交流和意见的统一上。与此同时,制作蚂蚁的程序员日益烦躁,觉得自己的工作没有难度,无聊,士气低下……

        换一种思路,从人类管理的角度来看问题。假设有一支庞大的军队(假设是一个集团军),司令官需要他的士兵列队,我们来为这个系统设计调度算法。先假设所有士兵跟蚂蚁一样笨,他们只能明白“站到司令部大门往东50米,往北100米的地方”这样的简单指令,请同情一下这位司令官,他不得不为每个士兵来指定一个位置,并且不得不研究列队的规则,他需要整天忙碌来完成这样一个庞大的任务(而且还不一定能完成)。他叹息道:哦,上帝……

        让我们设法来减轻他的烦恼吧,目标是让每个人都主动参与这个事件,不再那么被动,大家都努力完成自己力所能及的工作。于是我们授权各级指挥官让他自己的士兵列队,这样一来,司令官的工作简单多了,他发布命令:各位军长请注意,我命令你们列队,按照番号顺序,分布到司令部门口的空地上(假设这个空地足够大,姑且认为能够容纳整个集团军),各军之间保持50米间隔。

        接到命令以后,军长们开始忙碌,而司令官先生已经可以搬一把椅子坐到电话机旁,等待列队完毕的报告了。同样,军长要做的事情,也就是告诉属下的各位师长,让他们按照番号顺序列队,就这样,命令被传递到最下面一级。班长大喊:伙计们,按照个头排成一列,矮的在前面,高的在后面,前后间隔一米!于是,所有人站到了他应该站的位置,望着在短时间内迅速列队的整个集团军,司令官太满意了。

        我们发现了什么?很显然,下放权力的方式要省事得多,更关键的是,它使得每个人都做一定的事情,但是又不成为负担。在设计者思路清晰化的同时,负责为系统每个部分编写代码的人员也更容易享受到编程的乐趣,就算是最低层的程序员也有了发挥自己才能、用自己的思路去影响系统的机会,而且,系统集成的过程将变得更加简单。

        对于一名软件设计师来说,他的思想决定了他所设计出来的软件结构,将自己的灵魂注入到冰冷的代码中,这是一种艺术。然而,不同的人有不同的风格,设计者对于世界的认知方式不同,他们对于同样的需求,可能采用的设计方式也多种多样。

        (未完待续,下面会用我以前写的一个象棋程序为例,对照分析)