2007年10月9日星期二

.NET Framework 开放源代码

一些.NET Framework的源代码开放了,基于MS-RL许可,并提供调试整合到VS2008当中了。从旁观者的角度来说,这是Microsoft迈向开放与社区化合作的一大步,很多人也把这当作历史性事件,然而对于一般的开发者而言呢?这事情到底有多大影响力呢?我认为对于开发者来说,不同角色的开发者遭受的影响是不同的,并且整体影响是导致分工继续细化。

.NET最内层的本质是什么?Microsoft曾经非常引以为豪的COM,.NET只是这种思想一路实践并且进化而来的结果。.NET最开始设计为满足RAD的需求,以便吸引使用其他语言、框架的程序员转移过来,然而开放源代码后RAD的程序员仍然是RAD的,这对他们几乎没有任何影响。想象你是一个习惯于拖放一切的ASP.NET开发者,基本上不想写任何业务逻辑之外的代码,数据访问层用Typed DataSet或者Linq to Sql搞定,界面用现成的Control和Extender,Microsoft这次提供的源代码对你有什么意义吗?因为你不需要自己编写Control或者Extender,自然你不会花时间去了解有关的模式,也无须查看内置控件的代码。如果你调用内置控件出问题了,在Google以及调试内置控件之间,你显然会选择前者。因此,对于习惯于RAD的程序员来说,开放源代码这件事是没有任何直接影响的。

然而,有些间接影响是不能忽略的。前面提到了使用Google搜索问题的解决方案,然而Google自身并不懂得解决问题,答案其实来自与其他已经把问题解决了的程序员,因此这些源代码如果确实帮助了其他类型的程序员解决了问题,那么也就间接帮助了RAD程序员。

那么还有哪些类型的程序员呢?例如做稍微底层一些工作的,编写Control、Extender、HttpHandler、HttpModule等可复用组件以便为自己或别人提供方便的。编写可复用组件最糟糕的地方就在于它是可复用的——你永远不知道别人会将它以什么样的方式用在什么样的环境,因此按照一定的模式开发这些组件以便保证兼容性就很有必要,而模式本身最好就参考自.NET Framework内置的同类组件,除非你想更大范围地研究.NET Framework并重新发明轮子。因此研究与模仿内置组件的行为是组件开发者的必修课,而从ScottGu文章(Releasing the Source Code for the .NET Framework Libraries)中的截图看来,内置组件丰富的注释将有助于程序员更轻松地理解其原本的设计方式,从而更轻松地在自己的组件中模仿内置组件的行为。事实上,有很多内置组件是设计为对另外一些内置组件特别照顾的,这类型的耦合在Reflector中阅读代码时是最难以理解的,如果阅读有注释的代码相信会轻松不少。

最后,开放源代码可能将会导致对.NET Framework进行纯粹思想或理论作研究的人数增加。事实上,无论.NET Framework多么倾向于实用型,如果Microsoft需要获取来自社区的创新思想,还是必须吸引一群思想家的,否则大多数的社区创新都只是应用与应用方法,Microsoft还是独揽.NET Framework前进方向的控制权。这种中央集权有它高效的地方,特别是发展初期,Microsoft能够根据自己的实力战略性地安排新特性的研发顺序。然而Microsoft也曾经因此吃亏,例如ASP.NET 2.0没能引入AJAX支持,直到最后才急忙补上一个Callback特性,并承诺日后开发完整的AJAX库。因此,倾听来自社区的观点很重要,而要求社区有观点就必须先提供素材给他们讨论,开放源代码将能够激发社区对.NET Framework的研究热情并且提供更多能够作为反馈信息的新观点。

因此,就.NET Framework开放源代码这样一件事情而言,对于不同的开发者其影响的大小是不同的。同时我们也能预期Microsoft本身肯定也是最大的受惠者之一,否则以其智慧绝对不会做这样一个决策。

2007年10月6日星期六

Is following spamming?

一般来说,信息发布总有主动的发送方和主动的接收方,如果spammer是主动发送方,你可以积极拒绝;然而如果spammer是被动发送方,那么你可能就不容易留意到了。

从最普通的email spamming开始说吧,这完全是spammer主动发送给你的,因此你可以用过滤器过滤掉,或者看到标题就直接不看了。然后发展到论坛上的spamming,明明论坛是公共场所,大家都是平等的,但有些人就发一些spam上去,因此论坛上的信息对你来说不再是平等的,有些你一看到就会绕开。之后还有blog spamming,blog是你的,而spammer本应是被动的接受方,结果他却成了被动的发送方,每次你有新文章他就上来跟一个spam,因为你的文章是主导,所以spam变得不是那么不显眼。最后我要说的,就是别人在Twitter上follow你算不算一种spamming。

我昨天突然收到一个following通知,follower的名字是我不认识的,所以我就去看看那个人自己发什么信息,然后发现那个用户根本就没什么自己直接发布的信息,都是那种直接将自己blog的feed转发到Twitter并附上TinyURL的。然后我就想,他是不是再spamming我呢?至少如果他是spammer,那么这种做法是成功的。例如你写一个blog,你想让别人订阅你的feed吧,推广可不是容易做的事情。然而你把feed发布到Twitter,然后follow一堆陌生人,首先这些陌生人绝大部分都会因为好奇而看看你的Twitter,那也就相当于看了你的标题列表了,如果你的blog主题对他们有吸引了,这就成功吸引他们订阅了。就算不是这样,很多人也会出于“你认识我然后我又认识一下你吧”这一心态而在Twitter上follow你,那其实也就是订阅了你的feed,这样你也算成功了。

最后,我想说的还是一个观点,Internet是不需要直接实事实名制的,在用户使用Internet的过程中会逐渐发展出一套适用于Internet的信任与安全机制,也就是说每个人都粗略懂得在Internet上什么人能够信任什么事情是安全的。另外提供一篇相关阅读:使用实名SNS网站(校内、Facebook)请注意安全

2007年10月3日星期三

Leeching and Seeding

最近用TorrentLeech下载了一些东西,感觉比在TLF下载BTTEAM发布的还要爽,然而却只敢选择性的下载一些想要快速下载的东西,不敢说看到什么就立即想下。

TLF BT,几乎所有下载都是seeders明显小于peers,也就是说包括BTTEAM的发布者在内,只有少数几个人在seeding,其他多数人都是exchanging的,也就是说既download也upload,这也就是我们理解中P2P network的样子。然而大多数下载者都是下载完就关闭BT的了,完全不关心自己的上传下载比率(ratio),因为也没有理由去关心,反正总有人在那里seeding的,等seeders自然减少到零了一个torrent就逐渐过期失效了。

然而在TorrentLeech就不一样,除非是刚刚发布的torrent,或者非常冷门的torrent,其他基本上是seeders大于leechers。seeder大家都能理解,就是初始上传或者下载完了完全上传不下载的人。那么leecher是指什么呢?leecher是指完全下载的人,在leeching的过程中没有任何的义务,你可以什么都不干就享受高速下载。基于seeders大于leechers的事实,leeching的速度非常高,有很多个seeds抢着给你送数据。然而面对如此高速的leeching,却不是想下载什么就随便下载的,因为你必须很清楚之后才需要付出的代价。

正如TLF的新手指南所说,在国外是没有免费午餐的,你要下载就必须要有付出,就算你本身不参与任何协助0day发布的工作,至少你要在P2P network里面上传,上传下载比率为1.0是基本的要求。例如TorrentLeech,新注册用户的前4G下载是暂不计算ratio的,下载超过4G之后系统就会开始检测你的ratio是否达到最低要求的0.4,达不到的话就出warning。别以为warning好像口头警告那样随便,warning相当于黄牌,下一步就是ban出局,你想重新获取邀请注册TorrentLeech就很难了,并且邀请你的人可能也要负责部分责任。如果你的ratio在0.4到1.0之间,TorrentLeech的下载等候与下载并发控制机制会激励你尽量提高自己的ratio,最开始任何新增下载你都必须等48小时才能开始下载,并且同一时间你只能下载一个torrent的内容,然而随着你的ratio和总上传字节数提升,最终可以达到无需等待以及无限并发下载。

可能这也就是中国和美国之间差异的一种体现。在中国你可以不贡献,你名义上是exchange,但实际上就是真的在leech,并且不用遭受任何惩罚,于是所有人都在想尽办法的拼命抢夺稀有资源。在美国的话,你可以对政府有信心,你可以相信你需要的资源是补给充足的,没必要以各种恶劣手法去获取,然而你必须承担相应的责任,逃避义务的后果是非常严重的。而且由于公平对待也就不存在杀鸡儆猴的做法,你不能说因为你不是违章中比较突出的那个就抱有侥幸心理。

最后,补充一些纯技术上的解释。习惯使用公开型torrent tracker站点的人,可能不理解TorrentLeech是如何统计ratio,P2P嘛,我和别人交换了多少数据你怎么知道,我下载时你又如何得知我是哪个用户。TorrentLeech的做法是在torrent文件里面嵌入你的帐号指纹,之后你的客户端一旦联系tracker它就知道你是哪个用户了,从而控制你的等待时间以及并发下载。同时所有torrent都是禁止DHT等用户间直接数据交换的,所有数据流由tracker引导,这就可以正确统计数据了。你强行要开DHT的话,通过DHT上传的流量不计算入总数,而且如果其他人没有开DHT的话你就没有DHT下载优势。

2007年9月20日星期四

Binary Choice & reCAPTCHA

有些时候我们面临binary choice,也就是二元选择,的时候我们会认定只能选一个,因此必须牺牲另外一个,如果无法明确区分两者轻重,这将是一个极为痛苦的选择。不过我最近在读李中莹的《重塑心灵》时发现,原来我们应该积极去考虑如何能够两全其美,而不是被迫接受binary choice。

举个例子,为了防止有人使用bot连续请求某些web资源,我们可能引入CAPTCHA,也就是通常所见的验证码,以确认请求是真实的。通常我们也就默认了,越高级别的安全要求,就必须付出越多的代价,然而一个叫做reCAPTCHA的服务却不这样认为,至少它认为为提高安全性所付出的性能代价能做点别的有益的事情。

如果一般的验证码一样,reCAPTCHA也是提供一组处理过的英文字母,要求用户识别并输入。不过reCAPTCHA并不直接以零售模块形式嵌入你的站点,而以web2.0流行的API方式免费提供,你可以使用它的API为你的站点中需要的位置加入验证码。可能你会问,reCAPTCHA这样做有什么好处,因为它提高了你的站点安全性,却自己付出了性能代价,难道它在验证码下面显示一段广告?嗯……这是常人的思维,确实有很多web2.0服务通过附带广告盈利,不过reCAPTCHA可是Carnegie Mellon University的项目,所以不可能有广告的。一所大学完全出于计算机安全的研究提供免费的CAPTCHA服务?也不是,其实Carnegie Mellon University在大量扫描书籍,同时利用CAPTCHA提交者做人肉OCR。这时候就想通了吧,CAPTCHA虽然占用了计算机资源的同时,也占用了人的大脑资源,然而reCAPTCHA这个项目成功的让CAPTCHA提交过程中的资源浪费变成有效的资源消费。

根据reCAPTCHA的介绍,每天有六千万个CAPTCHA被人们识别,其中每一个大概消耗10秒钟时间,然而将这些工作时间聚集起来就非常多了。那么reCAPTCHA到底是如何工作的呢?当后台的扫描服务遇到一个无法识别的单词时,它就会和一个真正的CAPTCHA单词并列提供给用户输入,用户输入之后就仅对比真正的CAPTCHA单词,如果对了就当他输入对了,同时认为他输入的另外一个单词就是识别结果。对于一个无法识别的单词进行多次这样的操作,就能得到一个具备高确信度的结果。

如果你希望帮助该项目识别更多的信息,你可以在自己的网站上放置reCAPTCHA。对于WordPress和MediaWiki都有现成的reCAPTCHA插件,对于PHP、Python、Perl、Ruby等动态语言也能轻松找到reCAPTCHA库。另外reCAPTCHA还有一个MailHide的子项目,能够让你隐藏你的email,用户必须点击链接并输入CAPTCHA后才能看到完整的email地址。没错,就和Google Groups上面的效果一样,不过CAPTCHA用的是reCAPTCHA,并且也提供一堆现成的插件与库,有兴趣的可以去官方网站看看API文档。

2007年9月9日星期日

Minimal Blogging and Micro-Blogging

最近大家都在说micro-blogging,所以我也来说几句。为什么国内开始多人讨论micro-blogging了呢?估计是因为饭否在国内已经热了,能够有所谓的“现象(phenomenon)”了,同时国内其他Twitter的copy cat之间的竞争也越来越激烈了。然而我却一直用Twitter,无论身边都少人用饭否都不转过去。首先因为我比较无视copy cat,对我这样觉得原创很重要的人来说,copy cat服务通常看都不看一眼,知道是那么一回事就是了。每天有什么新的web2.0 startup出现,订阅TechCrunchRead/WriteWeb这样的web2.0观察日志就够了,基本上各种古怪的创意尽收眼底,一个copy cat根本不值得我花时间去看。一个新服务对比其参考服务至少要有自己的创意以及更好的市场定位,我才觉得有必要去了解,否则每天新增的web2.0 startup那么多我怎么可能了解得来。

回到micro-blogging的话题来,事实上我觉得Twitter这样的可以叫做minimal blogging了。minimal blogging这个想法来自minimal design,因为当你第一眼看到Twitter的时候,你肯定会说那是一个minimal design的。所谓的minimal design,也就是最小化设计,历史上曾经有一些人指责CSS仅能实现最小化设计,也就是只能由一些矩形的区域组成整个页面(参考Minimalism),更复杂的设计css就处理不好。而Twitter则被我称之为minimal blogging,因为它只能处理最小的最有限的信息,一篇最多就140个字符,不允许链接,不能是HTML或者别的markup language(例如ubb code)。虽然我一直在用Twitter,甚至还测试过从中国电信发一条短信给Twitter的英国接收号码要¥1,然而其实我并不觉得Twitter对我很有吸引力。一方面,我并不喜欢被flooding的感觉;另一方面,我想要的是micro-blogging不是这么minimal的。

对我而言,micro-blogging意味着我不需要想太多的就能发表,而且发表过程操作简单,然而同时要能够分享我能接触的各种信息。Twitter符合了前面两点,但是不符合最后一点,因为它无法发链接和图片,视频或者音频就更不要说了,不过因为我不是视频和音频爱好者所以这两项就免了。暂时而言,我使用Opera Community进行mobile blogging,也就是Cat in Mobile。它允许我在手机上即拍即发布,文字和照片都有了,对我而言就差一个链接了,不过mobile blogging不需要链接,要链接就通过feed和我的del.icio.us聚合一下好了。至于不需要照片的场合,Twitter也就将就着用了,因为无论如何同样是手机上的Opera Mini,Twitter操作起来还是比Opera Community要方便的,要知道手机上多填写一个表单就增加不少操作复杂度。

我真正向往的是Tumblr,一个tumblelog服务,然而我却从来都不使用它,最近也就仅仅导入了几个feed进去。其实这是很正常的事情,就好像除去Macbook之后VAIO真的异常吸引,然而即使我有钱都不会买VAIO,首先因为它确实烧钱,其次是日本产品的保修服务根本就不和欧美的同一个等级,意味着它的维护成本超高。日本产品,大至汽车小至PDA,停产之后都不会留充足的备件,如果你买的产品在保但是已经没备件了,那就无法修,因此我非常不喜欢购买日本产品,不用考虑保修直接吃进肚子里的除外。Tumblr看起来拥有我想要的功能,同时又保持micro,然而对我而言迁移成本实在高。在使用Twitter之前,我没有micro-blogging的需求,所以当时也没思考我能在我的Tumblr上发什么,然而在写这篇文章的同时,我在极力挖掘Tumblr的潜力,以及思考低成本的迁移方法,或者有一天我就迁移到Tumblr了。

最后一句话,为什么我向往Tumblr?因为国外的web2.0达人们有不少在都在用,例如Digg的共创人之一Kevin Ross

深入理解 ASP.NET 动态控件 (Part 4 - 解决问题)

前言

在开始写这个系列的文章之时,我想着必须深入介绍背后的原理,然后将所有需要的背景知识呈现到读者眼前,不过我现在发觉这并不是好的写作方法,要写下去对我自己来说难度也不少。最近受到Infinities Loop发布TRULY Understanding Dynamic Controls (Part 4)的刺激,我决定继续写这个系列的文章,并且领悟到了更多读者需要的是对问题的一种较为易于理解的解释,而非一种严谨的解释,因为前者更有助于读者解决当前问题并在再次遇到类似问题时自行推导解决。

其实我在本系列的第一篇文章就已经明确了怎样的文章才让读者容易接受,现在是我自己误入歧途了,所以必须纠正过来。第一篇文章的结尾建议读者自己阅读控件开发有关的书籍,之后就能完整理解和解决这个问题。实际上这是一个比较不合理的建议,大多数人并不可能花时间看完一本厚厚的控件开发书籍(ASP.NET 2.0的比ASP.NET 1.x的要厚了不少)。我需要做的不是复述书上的观点,那也不是我想要做的事情,真正需要做的事情是将书里面的观点浓缩为一篇文章,要让读者能解决问题的,推理看起来符合常识以至于容易接受,然而又比严密推理省下一大多文字。好吧,就让我们按照这种思路去看看如何解决一些常见的动态控件问题。

问题分类

这是受到Infinities Loop启发的,我们首先要将问题分类,然后逐个击破。这里的分类将最常见也最容易解决的排到上面来,然后逐步深入讨论。在开发过程中,使ASP.NET程序员想到要用动态控件的情景通常有如下几种:

  1. 你需要呈现不确定数量的控件,但这些控件是同一类型的
  2. 你需要呈现不确定类型的控件
  3. 上述两个问题的综合或嵌套
  4. 你需要开发自己的Web控件

在对问题进行分类之后,我们就容易逐个去分析解决办法了。由于分类是按照难度逐步递增的,所以这对读者来说应该是较容易理解的。

不确定数量的同类型控件

如果你要在页面上显示一个调查问卷,问卷的题目来自数据库,而任何问题都只有“是”与“否”两个选项,你决定使用RadioButton提供选项。这时候动态创建控件的念头应该仅仅是一闪而过的,然后你就决定使用Repeater。

如果你在这时候没有想到Repeater,或者任何的TemplateControl,那么你就需要重新熟悉ASP.NET的内置控件了。很多时候我们用多了GridView,特别是一直都用BoundField的话,就很容易忘记世界上还有TemplateField这么一回事。

那么为什么Repeater在此会是一个好的选择呢?首先,连最近基本的foreach迭代循环它也帮我们做了,我们仅需要指定DataSource,然后执行一下DataBind(),它就帮我们动态为每一个数据项按照模板创建控件。其次,对于PostBack之后数据发生更新的情况它能应付自如。为了说明PostBack更新数据造成的影响,让我们再来看一个例子。

首先,我们直接在Session里存放一个string[],内容为{"apple", "boy", "cat", "dog"},然后我们需要将它们显示出来,每一个项目显示为一个LinkButton,点击之后就在数组中将它删除。我们都知道使用Repeater或者GridView搭配ObjectDataSource做这样简单的事情是绝对没问题的,但如果我们手动编写动态创建控件的过程呢?

按照大多数人所理解的ASP.NET逻辑,首先应该在Load这一阶段遍历数组,然后为每一个数据项创建一个LinkButton,最后把这一切都附加到页面上唯一的那个HtmlForm上面去。删除怎么做呢?LinkButton实现了IButtonControl,所以可以添加CommandArgument属性,我们就把字符串保存进去好了。在OnCommand的时候就通过此属性识别当前需要删除的字符串,然后从数组中删除,并且还要在HtmlForm中搜索对应的LinkButton然后把它移除。

这时候你应该看看OnLoad中的代码是否记得为每一个控件的ID属性赋值,否则就会出问题了。页面一开始生成的结构应该是这样的:(左侧的是控件的ID,右侧是控件显示的字符串)

ctl01 ("apple")
ctl02 ("boy")
ctl03 ("cat")
ctl04 ("dog")

我们点击"boy",页面进行PostBack,然后Load生成同样的控件树,之后OnDelete删除ctl02,所以输出的控件树应该是这样的:

ctl01 ("apple")
ctl03 ("cat")
ctl04 ("dog")

我们这次点击"cat",页面又在PostBack,但接着Load生成的控件树就不同了:

ctl01 ("apple")
ctl02 ("cat")
ctl03 ("dog")

必须留意到控件的ID属性重新编号了,然而ASP.NET仅仅知道我们点击了ctl03,所以触发的ctl03的OnCommand,根据现在的ctl03的CommandArgument属性,删除了"dog"字符串。这就是所谓的问题了,无指定ID的控件会自动按顺序分配ID,因此ID具有了不确定性。

如果在OnCommand的时候,调用HtmlForm的Controls.Clear(),是否就能移除所有控件并且让ID重头开始编号呢?实验结果表明上述删除过程中第一次PostBack后会生成这样的控件树:

ctl05 ("apple")
ctl06 ("cat")
ctl07 ("dog")

也就是说,移除确实是移除了,然而ID编号没有重置,而是继续编号。那么Repeater是怎么做到的呢?为什么直接使用Repeater就没有任何问题呢?这个下一篇文章再说,我们现在专心来把问题逐个击破,现在你记住这种情况选择Repeater或者其他更高级的数据控件就是了。

不确定类型的控件

在面对此类问题的时候,首先问问自己控件的数量,如果数量不多,直接通过设置控件的Visible属性解决问题就是了。这也就是说,把可能要显示的控件都声明为Visible="false",然后在代码中判断当前应该将哪个显示出来。

如果控件比较多,然而还是能分组的,同一时间仅仅显示其中的一组,那么你应该考虑使用MultiView,这样你的工作将会轻松不少。事实上,能够使用MultiView解决的,都应该优先考虑使用MultiView解决,这比起自己控制哪一个控件显示哪一个控件隐藏要方便多了。其实MultiView所做的,也就是帮你控制控件的显示与隐藏。

这样做的性能如何呢?我们关注两方面的问题,一方面是服务器端执行的资源消耗,另一方面是传输的带宽消耗。我们先来看看服务器端执行的资源消耗吧,我们最常见的消耗应该就是数据控件操作数据库时的消耗了。在ASP.NET 1.x时代,我们没有数据源控件,所以必须手动进行DataBind(),这也就是说如果不手动执行DataBind()的话就不会进行任何数据操作,因此只要我们记得在数据控件不显示的时候也不要让它执行DataBind()就是了,那样就不会有性能损失。在ASP.NET 2.0当中,使用数据源控件的话数据控件是会自动DataBind()的,这时候会造成控件隐藏时的资源消耗呢?事实上是不会的,数据控件即使已经定义了DataSourceID属性,它也仅仅在自己第一次可见时才进行自动DataBind()。如果数据控件的状态是隐藏的(包括使用MultiView隐藏),它就不会自动进行DataBind()。因此,在ASP.NET 2.0中使用数据源控件以及MultiView之后其底层过程还是和ASP.NET 1.x手动操作的一样,就是少写一些代码而已。

我们接着来看看带宽消耗如何,因为隐藏的控件不输出任何的HTML,因此带宽消耗就是指ViewState了。控件隐藏后,ViewState是不变的,因此隐藏控件确实比完全不加载控件造成了更多的资源消耗,换取的是该控件的状态得以保存。一般来说,简单控件隐藏后多出来几十字节的ViewState是可以忽略不计的,整个页面中HTML缩进所需的空格也都几十上百字节了;但如果是复杂控件,拥有大量的ViewState,这时候你真的应该考虑动态加载了。

总的来说,面对这类问题时首先判断显示隐藏控件的逻辑是否复杂,控件本身是否复杂。如果是比较简单的情况,则直接使用MultiView解决就是了。如果是复杂的情况,那就应该考虑自己使用控件将此逻辑封装在内,而不是直接在页面上暴露这些复杂性。关于封装控件的问题,在下一篇文章中再讨论,因此我们继续看下一类问题。

既不确定类型也不确定数量的控件

有时候我们面对前面两类问题都有清晰的思路,但是面对复合问题就感觉很混乱了。例如还是一个调查问卷的显示,数据来自XML,问题类型包括单选和多选,每一道问题的选项个数也不确定,这时候怎么办呢?foreach嵌套foreach,外层迭代问题内层迭代选项,逐个CheckBox/RadioButton来生成?

这时候我们需要的是把问题分而治之逐个击破的思想。既然是上述两类问题的嵌套,我们就应该能够通过嵌套对应的解决方案来实现。对于这个调查问卷的例子,我们可以用Repeater来迭代问题,先把这个定下来,再考虑模板里面怎么做。模板里面需要显示的是一个不确定类型的问题,因此模板里面放一个MutliView,把问题类型的表达式绑定到其ActiveViewIndex属性上,例如单选题就是0多选题就是1。然后MultiView里面的两个View各自嵌套一个Repeater,第0个Repeater迭代选项并显示为RadioButton,第1个Repeater迭代选项并显示为CheckBox。就这样就完成了,我们没写任何一行后台代码,也没有动态创建任何控件。

然后我们来分析一下这个解决方案的性能。对比起动态创建控件,它所使用的控件确实是多了一倍,因为一道问题同时创建了两组选项,一组单选一组多选,只不过其中一组被隐藏了。然而隐藏掉的那一组唯一的服务器端资源消耗就是创建以及绑定,它们不输出任何的HTML,因为它们的值不会被改变所以也不会输出任何的ViewState,并且它们也不会触发任何事件,因此在对性能没有特别要求的情况下这样的性能损失还是可以接受的。至少,这比起你自己去研究ASP.NET页面生命周期然后自己写一大段代码来实现动态加载控件要好多了。

问题与实验

本系列上一篇文章的问题与实验一直没有解答,现在给出参考答案如下:

  1. 为Page增加一个ShowCheckBox的属性:
    bool ShowCheckBox {
      get { return (ViewState["ShowCheckBox"] == null) ? false : (bool)ViewState["ShowCheckBox"]; }
      set { ViewState["ShowCheckBox"] = value; }
    }
    在OnLoad的时候检测ShowCheckBox属性,如果为true则添加上该CheckBox控件。在Button的OnClick事件中,设置ShowCheckBox为true,并添加上CheckBox。记得这两处创建的CheckBox必须拥有一致的ID属性。
  2. 这是为了让ICallbackEventHandler的处理模型符合页面生命周期的模型。虽然Callback发生的时候,页面生命周期已经与PostBack不同,然而ICallbackEventHandler还是让Callback模仿了PostBack的页面生命周期。RaiseCallbackEvent相当于PostBack的Raise PostBackEvent阶段,GetCallbackResult相当于PostBack的PreRender阶段。前者负责事件响应,后者负责生成返回客户端的HTML代码。

这次想和大家讨论的问题是,你觉得你是完美主义者吗?面对上面的调查问卷需求,你会选择我所说的Repeater套MultiView再套Repeater的做法,从而避免写任何一行后台代码,还是会选择自己封装一个控件动态创建所有控件,避免任何不必要的性能损失?

最后,如果你喜欢本系列文章,并且不希望错过下一篇关于控件开发的文章,欢迎订阅我的blog:

在下一篇文章中,我将会介绍Repeater等数据控件是如何工作的,为什么它们能够轻松应对动态创建控件的各种情况,我们如何学习这些控件的设计模式并运用到我们的开发当中。

2007年9月6日星期四

Is this cheating?

终于把《The Game》看完了。不知道是不是因为它的写作风格比较pop culture,好像Hollywood大片的台词风格,所以读起来感觉非常好,也让人非常addicted,这点与读英文的技术书不同。这本书对我来说属于少数,我的意思是能够随之而来影响我现实生活的书,那当然是少数了。

首先当然是通过这本书发现了一条self improve的途径,因为the game本身不仅仅是围绕pick up这一个game,而是关于你的self-confidence。有些人天生就很有自信,能够随便地对别人说出自己想要的,或者仅仅是想法,而不怕遭受别人拒绝;另外还有一些人则相反,他们会害怕遭受拒绝,拒绝后也就接受了,而且心里还会不舒服上一会儿,以后再次提出的胆量也就少了。当然,人不是两极分化的,所以大部分的人分布在这两种极端之间,但总的来说还是有不少人对遭受拒绝感到恐惧。

读完这本书之后,就明白了PUA都想获得的一种本能反应——“我才不在乎呢”,反正被拒绝又如何,我又没有损失的。其实很多很理性的人都能够理解到没有损失这一点,但就是心理上不愿意接受被拒绝的可能性。对我而言,要去掉这种恐惧并不难,看完这本书之后我能够很快切换到那种心态,这正的麻烦在于——要降低被拒绝的概率,心态不是主要问题,技术才是主要问题。然而要练习技术就不再是那么容易,你需要花大量时间投入到实践当中,不断摸索,然而暂时对我来说我不觉得我有必要投入那么多的时间到与不同的人交流当中去。我还有书要翻译,我还有TOEFL要准备。

这本书引发的后继事件,包括我推荐了JimRobert来看这本书,然后我又在豆瓣上发现了ePIK也在看这本书。其中Robert和ePIK都和我一样是沉迷型的——这本书正是我想要的,我缺乏的东西里面有说,我渴望这种改变。而且我们这三个人还有一个共同点——technically a virgin。我想这正是我们渴望去改变的原因,因为我们都看到了前面有路可走,我们需要看清楚这条路,以及它能够怎么走下去。然而对于我身边的大多数朋友来说,他们对此都毫无兴趣,他们不是没有这个需求,而是忽视这个需求的存在,认为这不是能够通过一本书所展开的一组技能就能解决的。我们之所以相信这本书是有效的,是因为读过之后很快就开始分析身边亲眼目睹的一些这方面的成功人士的做法,并且发现这些天生拥有此项技能的人正好符合书中的描述。

有一件巧合的事见,我们在读这本书的时候,VH1的真人秀The Pick Up Artist开播了。书中有一些事情的描述,单纯看文本是怎么都看不明白的,但如果看到真人实践那就好理解很多了。另外网上有关的资料也不少,我们都搜集回来看看。有很多的技巧,其实不仅仅适用于pick up,还适用于日常沟通,能够让对方更乐于听你说,被你所吸引。

好了,有人看到这里肯定要说我文不对题了,起了一个完全无关的标题来吸引眼球。其实不是的,题目正是我接下来要讨论的内容。首先我要说明一点,现在我是支持life is all about experience的,你必须有所体验了才能更好地作出选择。就好像我希望能到不同的国家旅游一下,这样我才能更好地决定我到底想去哪里发展,随大流挤去美国并不一定是最好的。女人也一样,你要有充分的经验你才能够更好的选择,一开始就盲目相信one-itis是不行的,必须有重复的依据说明那真的是你的one-itis才值得你去追,然而依据来源于经验,而不是无端端的感觉。

好了,这时候问题就来了,就好像我只能告诉Singzy,"you are part of my experience, but you might not be the one"。这时候就违反了常人对relationship的理解。一般的理解是,因为我喜欢你,所以我喜欢和你在一起。然而我的逻辑是,我不确认我对你的态度,然而我需要和你在一起,因为这样才有助于我确定对你的态度。这就是题目所要问的,"is this cheating?"。

当然,其实我不认识这是cheating,这对我来说不是一个问题。因为我首先要有这样的看法了,我才能继续我的experience,否则认知失调带来的麻烦将更大,至少比选择女人的麻烦要大。The Game里面提到一件事情,就是假如你认为隐瞒着一个情人去发展或维持另外一个或者一些情人的关系是不道德的,那么你就不要这样做了,因为你自己都认为是错误的事情,无论如何都是错的。但是如果你好像穆斯林那样,认为这是对的,直接告诉对方我是一个穆斯林,在穆斯林的信仰当中一个男人要有三四个老婆才好,你是爱我的话就必须接受我的一切,包括我的信仰,这样才可能有出路。因此,我也就选择了把我所知道的以及我的逻辑告诉了Singzy。

2007年8月21日星期二

ASP.NET 3.5 的 ListView 控件与 CSS Friendly

之前在写CSS有关文章的时候,我就想写写如何使用ASP.NET控件能够更加CSS Friendly,更容易实现一些常见的页面布局pattern,然而之后就发现这并非那么容易的。说起来要让ASP.NET控简变得CSS Friendly很容易,直接使用ASP.NET 2.0 CSS Friendly Control Adapters就是了,然而事实并非如此简单。

CSS Friendly Control Adapters的不足

首先请允许我对这个CSS Friendly Control Adapters抱怨一下。我第一眼看到它输出的class名称我就觉得很faint了,举一些例子:AspNet-Menu、AspNet-Menu-WithChildren、AspNet-Menu-Leaf。如果你习惯了客户端代码一律使用camel命名法的话,你看到这样的命名就会觉得无法适从,你是要改变原有的命名法来迁就这些控件呢,还是让多种命名法在你的CSS文件中混排呢。如果需要改变这些默认的class命名呢?不好意思,控件自身的CssClass属性已经没有任何作用,因为控件输出的HTML结构都改变了,那些CssClass也就不再对应哪个HTML元素了。因此,如果你需要改变这些class命名,唯一的办法就是直接更改ControlAdapter的源代码,而class命名是以字符串形式硬编码在源代码中的,就算你用搜索替换你还是会害怕替换多了或者替换少了从而引入了更多的麻烦。

说到源代码,这些ControlAdapter的第二个麻烦也就浮现了——网站必须携带它们的所有源代码,而不仅仅是编译好的dll,而且这些源代码的可修改性并不强。为什么说可修改性不强?如果你有想过自己写一些ControlAdapter的哈,我想你已经参考过现有的那几个ControlAdapter了,你会发现编写ControlAdapter严重依赖于你对该Control本身的理解,不仅仅是对Control公开部分的了解,还需要对Control内在逻辑的深入理解。因此,要么你是Control的作者本身,要么你就细看过Control的源代码,否则不可能写出ControlAdapter,甚至修改已有的都很难。

因此,CSS Friendly Control Adapters是一个非常之鸡肋的选择,我们不如向前看,看看Microsoft在ASP.NET 3.5中为我们提供了什么。

ListView以及全新的TemplateControl形式

ListView是ASP.NET 3.5新引入的一个控件,如果你还没有使用上Orcas,或者没试用过这个控件,那么不妨看看ScottGu的介绍性文章:The asp:ListView control。这篇文章详细说明了如何先设计一个原型页,然后设计LINQ to SQL以便获取数据,在将数据绑定到ListView上面,最后还加上DataPager分页。我们不需要看那么多,看ListView那部分就是了,看看声明ListView的代码。

如果你熟悉之前Atlas提供的Sys.UI.Data.ListView,那么你一定会觉得这两个ListView很相似。与之前的TemplateControl(例如GridView)不同,ListView不再直接输出容器本身的代码,而提供了一个Template给你自定义容器,你可以在这个Template中自由编写你的容器代码,它可以是<table />,也可以是<ul />或<ol />。之后项目的Template也是允许自定义的,对应<table />的自然是<tr />,而对应<ul />与<ol />的则应该是<li />。因为这些都是你手动编写的HTML代码,所以你可以随意地给它们设置class属性,从而让你能在整个网站中保持命名风格一致性。

Web Form的屈服?

ASP进化到ASP.NET的时候,好像Win Form那样的拖放控件支持成为了最大的特色,然而现在Web Form的编写方式又变回和其它服务器端脚本语言(例如VBScript)差不多了。以前ASP的时候,不就是自己写容器的HTML咯,然后用<%For ... Next%>把项目HTML圈起来,现在改为叫做模板其实没什么差别啊,况且其他服务器端脚本语言都有类似的写法,不过可能是helper函数或者别的称呼,都差不多。

因此,事实证明除非放弃对HTML细节的控制权(而这又难以做到CSS Friendly),否则对于大多数服务器端语言来说声明数据表现模板的方式都是类似的,没有更便捷的方式了。能够省事的是数据访问方法,从ADO进化到ADO.NET,从Typed DataSet到LINQ to SQL。将来Microsoft是否会发布更多类似的TemplateControl还很难说,因为ListView已经有非常高的可定制性,原来用来表示二维表数据结构的DataControl都可以用它作为替代品,同领域的控件已经没意义了,不像以前要分开几个DataControl了。我觉得接下来最好能看到一个取代Menu的CSS Friendly Control,因为Menu所表现的数据结构不是二维表,而是树,有必要为这种数据结构提供一个能准确声明HTML细节的控件。

最后,如果你对我的blog中关于ASP.NET与CSS的文章感兴趣的话,可以考虑订阅:

2007年8月13日星期一

买了两本 Photoshop 的书

两本都是《选择的艺术》这个系列的,其中一本是《Photoshop CS图像处理深度剖析》,另一本是《Photoshop CS图层通道深度剖析》。买Photoshop的书,是因为工作中要用到,作为一位web设计人员,单纯需要编写代码的工作暂时还不存在(虽然Microsoft的产品线设计尝试让设计师与程序员能够低耦合度合作),因此学好一些基础的设计思想与设计工具运用知识还是有必要的。

例如Google的Webmaster职位,要求就包括Adobe Photoshop操作知识。我暂时只能用Fireworks做一些简单的mockup,mockup的素材没办法自己用Photoshop处理,这就大大限制创意的发挥。另外我还想去学学摄影,因为在我还不能什么都用Photoshop画出来之前,素材的获取有时候就必须依赖于实物,看来要补的知识还真是一大堆啊。

2007年8月8日星期三

为什么 Flixster 比豆瓣更好

虽然我主要适用豆瓣记录书、电影、音乐,然而用过Flixster之后就为它所吸引,希望豆瓣能够加上Flixter那样的功能。

在我认真使用豆瓣之前,Piggest跟我说过豆瓣的优点:在你注册并添加少量已经看过的电影后,你会发现你想继续添加的电影通常已经出现在“豆瓣猜你会喜欢”的列表上,这时候添加就方便很多了,因为无需再按名字搜索,直接点击就是了。然而Flixter更先进,它根据大家看过电影的历史分析出所谓的经典电影,也就是人人都看过的,没看过至少听说过的,在你注册后就提供这些经典电影给你评分。假如我没看过一部电影,而且也不想看,怎么办?Flixster比豆瓣多了一个“不感兴趣”的选项,这应该算是一个更体贴用户的设计,而这个设计豆瓣后来也加上了,不过不是一个直接的选项,而是你可以选择隐藏“豆瓣猜你会喜欢”列表上的推荐项目,那也相当于是不感兴趣吧。

对于一个Web2.0的网站来说,数据是很重要的,掌握的数据越多,就能越有效的服务现有用户,对新用户的吸引力也越大。在获取数据方面,Flixster的做法显然比豆瓣更好,对经典电影的评价几乎人人都有,而此时以推的形式主动要求用户评分并不会让用户觉得觉得不喜欢,反而提供了便捷,即使有用户不喜欢这样,他也可以选择之际跳过这个步骤。类似的,其实很多Web2.0网站都可以在用户注册的过程最后加上一个可选步骤,要求用户提供更多信息,而网站要设计好获取信息的方式,让用户觉得不用动脑筋就能填写,并且提醒用户在这里填写能够更便利更快得到全面的服务。

2007年8月1日星期三

今天开始翻译 Prototype and Scriptaculous in Action

感谢Dflying Chen的联系与推荐,让我有机会为图灵公司翻译Prototype and Scriptaculous in Action的简体中文版。这本书的作者包括Ajax in Action的作者Dave Crane,同时作为引进大陆的首本关于Prototype的书,因此估计将会热卖。

基于上述因素,翻译此书时我必须特别小心,避免任何翻译错误甚至仅仅是可能引起争议的地方,因为一旦这书上市了必定有不少读过英文版的人来读中文版,接着就可能指出他们认为翻译得不够好的地方,即使那个地方是纯粹文学性的并且与书中讨论的技术细节没任何联系。现在是Web2.0时代,你犯的任何错误都将被别人永久的记录在Internet上,如果第一本翻译的书出问题了那么将来就很难再被信任以担任同类工作,因为读者认为你的翻译不好的话出版社就敢让你翻译。实际上不仅仅翻译工作如此,现在的Internet逼着你我以及任何一个人做事时都必须加倍小心,面对公众所犯下的错误都将被一大堆人记录到他们的blog上,这种负面影响难以用你的个人之力以消除。当然,事情也有好的一面的,如果翻译做好了将能得到相当的赞誉,这种来自公众的赞誉也不是别人轻易能够克隆的,因此也有相当的含金量。

最后,如果你希望对我的翻译工作提供任何的建议或支持,或者你有兴趣和我合作翻译,欢迎留言或联系我。直接在blog回复留下联系方式的话,我将会主动联系你。

2007年7月31日星期二

理想的 ASP.NET AJAX (Part 2 - Server Centric)

使用ASP.NET的话……

ASP.NET的最大优势就是组件化,在UI上更明确地说就是控件化,但这却为AJAX带来了不少问题。

首要问题是输出HTML不由我们控制。复杂的GridView不说,我们就来看简单的CheckBox,在你不对它设置任何样式属性和文本时,它是一个单纯的<input />,加上文本的话文本会被放在<label />中以便点击文本与点击<input />等效,如果再加上样式属性的话外围就再多一个<span />用于设置样式。如果你获得了一个CheckBox的ClientID,那么通过$get()(指document.getElementById操作)获取到的是<span />还是<input />呢?答案是那个<input />。

这时候真正的问题就出现了,假如我有一组CheckBox放在一个大的<span />容器里面,当我在客户端选中一个CheckBox的时候它就从这个容器里把自己删除掉(然后转移到另一个<div />容器中)。我们关注的只是这个<span />容器内发生的事情,当我们通过$get()获取到CheckBox对应的<input />后如何知道它的parentNode的<span />是属于CheckBox的呢还是作为容器的呢?这个问题可以通过对作为容器的<span />添加id解决掉,然而我们仍需要手动判断<input />的parentNode是不是CheckBox的一部分以便知道是否要将它也一起删除掉。当然,你会说我的CheckBox不设置任何样式属性,不存在此问题。然而你能够确保这个CheckBox永远不会被设置样式属行吗?甚至可能是通过Skin而被“意外”的设置了样式属性。总而言之,一个CheckBox是否有<span />以及是否有<label />都是不确定的,而且是不正交的设计选项——有很多不同的因素可能导致它们出现。

这类问题在我们仅仅针对ASP.NET服务器端编程时可以完全不管,而且还很爽的样子——看看,我们可以全然不知道客户端发生什么事的前提下,甚至连输出什么样的HTML也不用管,也照样能设计交互式的Web。然而当我们需要考虑客户端交互的时候,这就很不爽了,ASP.NET控件这种完全隐藏客户端细节的做法让我们难以对输出的HTML进行DOM编程。

通过ASP.NET AJAX解决?

在Atlas出来的时候,我确实希望过能够在一定程度上解决上述问题,最多我就由server centric改到client centric好了,并且我也一直尝试着用Atlas进行client centric的实验性项目,然而得出的结果一直都不怎么好。

我们继续以CheckBox为例吧,不过这次改变对象为客户端的CheckBox,也就是由<input />初始化而来的Sys.UI.CheckBox。Atlas在设计的初期为这些控件考虑了很多,虽然当时控件仅有简单的几个,这里面最强大的设计思想应该就是绑定(bind)了,允许用户自行定义绑定的实现方式,而不仅仅是如同ASP.NET服务器端那样仅仅支持简单的双向绑定及自定义的单向绑定。

然而很快Atlas进入了Beta阶段,为了1.0的及时发布中心转移到了UpdatePanel等的服务器端控件上,客户端控件再也得不到重视,功能不再更新,甚至为了兼容服务器端控件的更新而被迫放弃部分原有的客户端功能。这时候使用ASP.NET AJAX进行client centric开发就变得越来越不可能了。

另外,使用ASP.NET AJAX进行client centric开发还面临一个不够search engine friendly的问题,因为数据都是输出到客户端再展示的,因此HTML本身并不包含多少有语义的内容。我曾经尝试做一个类似WebPart效果的门户页,完全不使用WebPart而使用Atlas自带的拖放支持实现是没问题,虽然其自带的拖放并不完美而必须手工添加不少东西,然而我真正面临的问题是页面第一次输出应该输出什么。

方案1是真正的client centric,好像GMail那样,一出来是空白页,显示loading,这时候通过XHR去和服务器沟通然后再添加内容到页面上,这显然是完全不照顾搜索引擎的做法,对于GMail这样本来就不应该被索引的页面当然没问题,但是大众网站这样做就不好了。

方案2是服务器端先根据用户的Profile生成用户最后一次拖放保存的页面,之后的拖放及保存工作才由客户端负责处理。然而这意味着同一套逻辑我需要实现两次,可拖放的部件在服务器端要是一个Control,在客户端同样要是一个Sys.UI.Control,同事都具备串行化为Profile已经从Profile并行化还原的能力,这样工作量就大大增加了。

之后,从ASP.NET Control Toolkit我们可以看到,客户端Control的地位已经逐步被Extender取代了,因为Extender可以被看作是一组由Behavior、Web Service等元素组成的部件,它不再关心DOM元素在客户端作为一个Control的完整性和独立性,而仅仅是考虑这些DOM元素应该表现出来的行为从而将这些行为附加到DOM元素上面去。这样思想让我们面对上面二选一的情况时有了更好的选择——在服务器端应该注重Control的整体,而在客户端则仅关注Control输出的DOM元素应该具备的Behavior。因此,在上例中我不再需要管一个可拖放部件如何作为一个客户端Control存在,我仅需要知道这个部件是一个<div />元素并且它具有可拖放的Behavior就够了,而无论何时代表整个部件包括其内容的只有服务器端Control。

这样看来,如今的ASP.NET AJAX已经有相当的价值,前提是你有能力针对你的应用开发相应的Control和Behavior。至于未来的ASP.NET AJAX能够为开发者再提供多少便利,这已经很难说了,因为继续在ASP.NET AJAX上面增加client centric的支持,倒不如把精力投入到Silverlight的研发上面去,ASP.NET AJAX很可能不会再有重大更新版本,能把ASP.NET Futures中有价值的AJAX功能都RTM就已经算好了。

最后,如果大家觉得这个系列的文章好的话,欢迎订阅Cat in Chinese(feed)或Cat in dotNET(feed)。将来话题可能将转移到Silverlight,一个我认为比ASP.NET AJAX更具潜力的框架上。

2007年7月30日星期一

理想的 ASP.NET AJAX (Part 1 - Client Centric)

怎样的AJAX才算是理想?

要说什么是理想的ASP.NET AJAX,就要先说说什么是理想的AJAX。事实上AJAX最不理想的地方在于search engine friendly以及bookmarkable,这两个问题有一定的相似性,要解决并不难,只是每一个系统中实现起来都不一样,因此难以提出一个统一的patterns来解决。

首先说说search engine friendly这一点吧,实际上使用了AJAX的站点有很多信息是搜索引擎无法索引到的,因为页面上部分的信息是在用户进行操作后才显示的,显示的这些信息有可能是固定的也有可能是经过XHR(XMLHttpRequest)查询服务器后动态显示的。如果是固定信息,那么在生成页面时就要考虑这些信息是否也应该输出为静态HTML。那么什么情况下该输出为静态HTML而什么情况下不该输出为静态HTML呢?我觉得应该看隐藏的内容是否就是页面语义的一部分。例如页面用于显示一张集体照,当鼠标指向照片上不同人物时将显示他们的姓名和有关的介绍,那么这些信息就应该输出为静态HTML了,因为它们是页面信息的重要组成部分,并且这些信息应该被搜索引擎索引到。

当然,事情并非任何时候都那么简单,有时候我们并不能简单判断某些信息是否应该属于一个页面,或者说一个URL。例如你使用一个页面来展示你的gallery,但并非一下子显示所有的照片,开头显示随机的10张图片,然后允许用户输入/选择tag来显示对应的照片,如果当前显示的照片超过10张还会自动分页。这时候怎么办呢?整个gallery输出的一个静态页包括所有的照片以及描述?这看起来不太可行。假如你的gallery有100个tag和1000张照片,同时这一个页面成功被搜索引擎索引了,用户通过搜索引擎来到gallery页,他怎么找到搜索引擎上匹配的图片以及对应的描述?他根本不可能知道他要找的信息藏在哪里了,100个tag中可能只有几个能让他想找的信息显示出来。

这时候比较好的做法就是将tag分离出来变成独立的页面,或者说是URL。gallery首页上的tag对于用户来说是交互式按钮,在不刷新的情况下直接筛选对应的照片;但对于搜索引擎来说那是链接,链接到代表该筛选状态的页面,并且在该页面上才索引到照片和描述。这样当用户点击特定的搜索结果是,进入的是已经筛选了的gallery结果页,也就必然能看到他搜索命中的信息。同时这也增加了bookmarkable的可能性,因为一个URL已经不仅仅代表一个页面,而代表一个页面的特定状态,因此状态变得bookmarkable。然而这时候普通的筛选结果还不是bookmarkable的,因为在进行根据tag筛选后URL是不会被改变的,因此我们必须通过JavaScript改变URL才能让页面变得bookmarkable。

与理想的距离有多远?

我们面临最大的问题就是URL与状态的对应,以及有关的存取。我们希望每一个indexable & bookmarkable的状态都对应一个URL,而且最好是meaningful的URL。好吧,我们先舍弃meaningful这一点来探讨状态问题,实际上我们已经有一些方法来保存状态。例如ASP.NET提供的ViewState就是一个很好的例子。很多ASP.NET的新手可能会觉得要理解如何正确使用ViewState并不容易,其实它就是用于保存view的state的。整台计算机其实也就是一个state machine,不过我们当前所关注的是与view有关的state,所以称之为ViewState。ASP.NET开头的策略是在服务器端处理一切,所以发明了ViewState并尝试让状态在客户端仅作持久不作修改。然而我们需要的正好是相反的,我们需要一个在客户端能修改的状态,并且以URL作为持久的方式。

首先,我们不可能好像ASP.NET保存ViewState那样保存数据到URL当中。即使我们舍弃了meaningful,ViewState的容量还是会超过URL的长度限制,因此希望好像ASP.NET那样每一个控件各自汇报自己的ViewState然后页面负责统一持久到URL是难以实现的。(当然,在ASP.NET AJAX中尝试一下这样的实现也未尝不可,虽然其提供的控件少之又少。)

既然我们不能够好像ASP.NET保存ViewState那样“放肆”地利用URL进行持久,那么我们就必须针对特定的应用来考虑持久的方式,这正是无法开发一个通用框架实现此功能的原因。例如上面所说的gallery,我们要人为的考虑使用tag作为区分view的一个状态参数,接着我们可能还要考虑当用户选中多个view之后会发生什么事,是不是简单的在URL里面叠加多个tag呢?例如gallery.html#fun+event的样子?那么它和gallery.html#event+fun是等效的,而两个URL会不会导致PageRank的分散呢?之后又如何加上分页参数呢?这一切问题不是没有答案,而是它们的答案都太具针对性了,因此不具有通用性,难以做成一个可复用的框架实现。

因此我们在这里能做到的最多就如ASP.NET Futures或者别的框架提供的history功能那样,提供一个有限容量的string空间给你,你要自己决定如何把状态转换为string然后提交给它保存,之后它保你实现跨浏览器的Permanent URL和history支持。

到此为止,我们站在client centric的角度讨论了AJAX现在面临的一些重要障碍,以及潜在的解决方案。下一part我们将从server centric的角度来继续研究这个问题,并探讨ASP.NET是否能在AJAX方面做得更好,又或者是别的Microsoft技术。继续关注本文章系列,欢迎订阅Cat in Chinese(feed)或Cat in dotNET(feed)。

2007年7月25日星期三

Hong Kong Trip

刚刚从4日3夜Hong Kong trip回来。事实上期末考的时候我就计划要去HK看看,一方面是去参观几所大学,例如科大、港大、中大啦,另一方面是去了解一下香港是一个怎样的城市,例如市民生活如何之类的。所以一放假我就去办通行证了,上个周末终于去了一次HK。

Day 1

第一天我们选择了不搭直通车而搭到罗湖,然后过关后转东铁到大学站,参观香港中文大学。中大座落在山上,面积超大,而且那天没有人带着游览,所以也不知道看什么好,很多区域也不能进去看,只是感觉的学校真的很大。很多教学楼都是傍山而建的,因此两边的出口在不同的楼层,高度差可以导致两个入口相差5层之多。

中大游览之后就去了尖沙咀看科技馆,正好碰到了“飞龙在天”的恐龙展览,于是就马上买了半价的学生票冲进去。有寄存服务真好,可以把书包扔下自由自在的看科技馆里面有趣的展品。展品有很多都是可交互的,虽然弱智了一些,因此很多小朋友在那里玩。

在科技馆中,和Lucy约好了7点到中环站,于是看完我们就搭地铁过去啦,本来想去ferry一下的也没时间了,结果去到Lucy才说最好等埋她同事然后带我们去吃好野,早知道就去埋ferry啦……

晚餐是去吃牛扒,好好味哦!然后回Lucy住酒店Poker,并且Lucy人品暴涨说可以让我和Singzy睡她的房间然后她睡sofa。说到Poker嘛,我发现自己不擅长这类要求下注的游戏,因为我总是没办法在短时间内算准可能出现的情况,我还是适合玩长线投资的game多一些,咔咔……

Day 2

第二天起来是Lucy还在睡觉中,貌似正中了“Hongkongese没有星期六早上”这个说法,因为大家星期五晚上都是用来娱乐的,那么星期六早上就是在睡觉中度过的。我和Singzy独自跑去Hollywood Rd,发现原来半山行人电梯早上是下行的,非常幸运,那就搭行人电梯下去了,结果原来Hollywood Rd也在“睡眠”当中——基本上没有开铺的。

既然Hollywood Rd没东西看,就搭车去香港大学咯,感谢昨天那么多人告诉我们23能到港大,就去坚道搭23了。去到港大东闸下车,发现10:00会有guided tour喔,而当时是9:00,面对的就是港大美术博物馆,当然是选择进去走一走并且等guide的出现啦。美术馆的叔叔很nice,后来出现的那位guide也很nice,于是我们就跟随着guide和一大堆同行的小朋友们游览了港大。

港大之后就是去深水埗看电脑,看到3款macbook的售价分别是8600HK$/10200HK$/11700HK$,也就和macx的代购价差不多,因此估计macx买得多所以买入价比单台的零售价还要低吧,这样它才有得赚。另外看到一本台湾翻译的Transcending CSS,译作《超越式CSS》,要180HK$,没有狠心买下来,回来发现这本2006年底出的书竟然emule上还没有pdf下载,极度后悔中……

午餐吃的是McDonald's,发现mid-size的薯条包装虽然和广州的差不多,但分量多很多啊,简直要撑死了。之后下午就去了黄大仙和科大。

香港科技大学的超级无敌大海景就是在吸引死人了,教学楼也是傍山而建,不过是直接面朝大海,而且整体规划所以效果很好,每一栋楼都能望海。可能因为科大已经考虑都会有相当多的游客来参观,所以有专门的纪念品商店、展厅等等,图书馆还在搞校史展览,新建的学校果然思想也新潮很多,很重视publicity。

在科大的图书馆看校史展览时很郁闷,每次一站定手机就开始震,然后冲出门口接电话,讲完又走回去。其中包括我姨妈的电话,约定了晚上到上环,于是就离开了科大。由于时间还有多,所以就没有搭地铁回香港,而是去ferry了——从尖沙咀的天星码头到中环码头。在渡轮上吹海风感觉很爽,可惜就是很快就到对面了。

Day 3

在罗湖过关的时候看到有书展的广告,所以第三天就去会展中心看书展了。超多人,龙尾排到很远,不过进场很有效率。实际上对比香港和广州,香港很多事情都要比广州高校很多,例如交通就如此,香港的街道上不会有“散步车”,能开的就很快地开过去,路上的行人能通行的时候也走得很快,总体来说应该是整个城市的节奏都很快吧。在广州的话,大家都习惯了慢慢来,不要那么紧张和冲动,所以效率也就完全不同了。

书展总共有5个hall,走完花费了5个小时。香港人看的书和我们看的书是不同的,貌似他们更多看书是为了休闲,至少他们平日工作已经够紧张的了,所以书一定要有相当趣味性才能看进去,太过单调的理论书是没有人买的。我不是说数学书,而是说例如基金书之类的。现在大陆够多人买基金书啦,很理论的书都有人买,但这在香港可能就是行不通的。

在书展当中我什么书都没买到,走了5个钟头啊……感觉比较可惜,真应该下狠心买一两本大陆绝对买不到的书回来看看,虽然价格肯定是大陆版的2~3倍,假如大陆会出版的话。promotion做得最厉害的一本书就是Harry Potter啦,最后一本哦,而且就在星期六全球同步发行,在走道一路看过去都是Harry Potter的标价,而且一个比一个低。另外一本很鬼死多promotion而且题材也很有趣的书就是台湾翻译的《把妹达人(The Game)》,内容看起来也很有趣,不过基于我不舍得花钱……所以还是选择了回来再找pdf。

书展之后超级疲劳的再次来到McDonald's,又是超大分量的薯条啊……已经吃到闻到薯条味就想呕了,一个月内不要再吃薯条。这餐是下午吃的,都不知道算午餐还是晚餐了,中午仅仅就在会展了面吃了几个墨鱼丸啊。

在McDonald's有很多菲佣,其实满街都是,因为是星期天,她们都出来聚会。忍受够吵闹的McDonald's之后我们决定跑去山顶看看,由于第一天碰到两个台湾来香港旅行的人问我们中环站如何走到缆车总站,因此我们步行到金钟站然后打一个站的地铁到中环站,打开guidebook一看才发现——原来金钟站和中环站步行到缆车总站的距离都是15分钟左右,晕掉了……走到缆车总站开始排队,我们开头想着去山顶看日落的,结果上到山顶看到的是“日落ed”——也就是说太阳刚好下山了,不过能吹吹风也不错。

在山顶也没有吃什么正经的晚餐,Starbucks喝了两杯东西就又要开始排队等缆车下山了,然后还是搭23回到坚道然后经半山行人电梯回hotel。貌似我们只知道23这条线,哈哈……

Day 4

最后一天,其实已经没有任何listed的行程了,于是大白天都在hotel里睡觉。Lucy说过中午请我们吃饭,所以11:30才起床去找Lucy。Lucy说搭12能够去到她上班的遮打大厦,我们从hotel下来连站牌都还没看清楚就有12停在身边了,那就上咯,看到哪个站合适就在哪个站下吧。HK的bus都当你是local,根本不报站,除非你主动问过司机你应该在哪下然后请他提醒你。作为一个旅游城市,HK路面的标示都超明显而且对游客有帮助,就是bus不报站这一点对游客很不friendly。

事实上Lucy也不知道去吃什么好,所以在兰桂坊随便挑了一家店就坐下来了,发现要一个4人套餐分量充足(废话,我们才3个人),而且每一款都很好吃。吃饱之后就和Lucy say goodbye然后去香港历史博物馆了——我们离开香港前的最后一战啊,因为博物馆可以寄存所以很适合背着行李去。

历史博物馆里面也碰上了guided tour,虽然整个tour并不让你有多少时间看清楚展品,不过guide的讲解也很精彩,能让你听到很多展品街市上没有说的事情,可惜我们时间有限,完成1个半钟的tour之后就没时间返回开头细看展品和演出了。很有趣的一件事情就是,两次积存行李时都被别人说“怎么你们的书包这么重啊”,其实就装了4天旅行的一些必需品啊,而且我们觉得也就是acceptable的程度,而非overloaded,看来HK的小朋友们的书包确实也不重啊,否则他们的daddy、mommy怎么会没见识过overloaded的书包呢。

17:55那班直通车,我们从红勘站离开了香港返回广州,不过因为列车晚点实际上我们18:30才上车。回到广州发现整个pace又慢下来了,所有事情又回到了我们习惯的速度。

2007年7月24日星期二

MVP 购物 coupon

使用Company Store的coupon购买的礼品收到了,我买了$90的硬件,包括webcam、headset和keyboard,剩下的$60用来买一些小礼品。小礼品大多数都是$10一件吧,买了一个大杯子给我爸,一个水壶给我妈,一个水瓶给Singzy,一个笔记本给Piggest,还有一件衣服是我自己的,之后剩下几$就买些钥匙扣之类的琐碎物。

我觉得这算是一种分享happiness的途径吧,而且这是一种保持自己happy的有效方法。当你拥有一些resource的时候,可以考虑不是把它们独占着,而是分享给更多人,同时又不引起什么竞争或者冲突,那就是最好的了。

2007年7月16日星期一

分享或索取新成立网站的邀请

如果你属于关注Web2.0的人,常常希望了解一下startup(新成立网站)的创意以及实现效果,又或者你仅仅是非常geek非常喜欢试新,你都经常需要各式各样的邀请以加入到所谓的private beta当中去。正所谓private beta,一开始的邀请当然只有开发者身边的朋友能拥有,而你和他们的距离是未知的,所以只能选择等待。

之前曾经有一些网站尝试去做邀请共享的服务,当时最热门的当然是GMail这样的邀请啦,于是大家就都把自己的GMail邀请链接投放到共享网站上,需要的人就直接获取。现在的大热变成Pownce了,甚至有人在eBay上出售邀请,不过也有免费的获取方法,那就是InviteShare。这个网站不仅仅共享Pownce邀请,还共享非常多其它网站或服务的邀请,例如JoostWeeWar的。你需要做的,仅仅是在InviteShare上面注个册,然后找到你想索取申请的网站,把自己添加到等候列表上,之后就等email通知吧。如果你觉得排队的速度太慢了,可以通过发送邀请来提高自己的优先级——从你手上成功发送出去的邀请越多,你排队的优先级就越高。

之所以说InviteShare,是因为我觉得它是一个做下游服务做得非常成功的例子。当某一种服务的数量越来越多的时候,例如需要邀请的Web2.0服务,那么它们的共性就会表现出来,这时候也就可能形成一个新的下游服务市场。这种情况往往是可预见的,例如Web2.0的private beta成为一种流行的服务推广方式,那么在恰当的时机推出下游服务也就是可行的。当然,事情的发展往往都是很快的,恰当的时刻一眨眼就过去了,因此我们需要一种相当敏捷的开发方式来实现我们能够提供的服务。

2007年7月14日星期六

今天收到 MVP 礼包了

礼包的寄送过程有点混乱,不过最终还是送到我手上了。拆开DHL的包装后,看到的是漂亮的MVP礼包盒子,诺大的纸盒打开以后发现包括一张证书、一枚别针、一本说明书和一个皮革质地的小盒子。

证书是很正式很传统的样子,有点像英文的毕业证书那样子吧,看字体就很有中世纪的feel。别针是MVP的徽标,比团徽重多了,针也粗多了,所以别在衣服上貌似不太合适,也不知道可以别在什么东西上。说明书则包括很多关于MVP身份的介绍性材料,当然还包括提醒你去消费那$150的Microsoft Company Store购物卷。事实上,真正有价值的东西都藏在皮盒里面,包括参加会议时可挂在脖子上的namecard(当然是注明MVP的了)、一个有MVP印记的皮质名片夹、一叠有MVP水印的memo纸、一支带激光的圆珠笔、一只皮质外壳的1G U盘。

对我而言,最有价值的应该就是那支笔了,以后做presentation就可以用它的激光在屏幕上随便指,这点比较爽。U盘是其次,我现在有个80G的移动硬盘,也有一张1G的SD卡可搭配读卡器使用,所以U盘并不显得是什么必须品,不过1G的空间以及漂亮的外表,还是很让人喜欢的。

差点忘记了说,在礼盒的底部有一个信封,装着的就是一式两份的NDA(Non-Disclosure Agreement),也就是保密协议。签完之后放到附送的信封里,直接投到邮筒里就是了,因为信封上说明了由收信方付费的。

2007年7月2日星期一

Microsoft MVP Award received

昨天晚上收到了邮件,告诉我获得了Microsoft MVP奖励,然后异常兴奋。要做的第一件事情当然是告诉身边的各位好友啦,特别是感谢那些在此事上曾经支持我的人。事情有时候很奇怪,至前几年热衷于上CSDN回答问题,去年开始将.NET有关的帖子集中到博客园并提升更新频率,这些都是比较“无心插柳”的事情,最后反而有结果了。

或许应该这样说吧,一些事情虽然我想要得到结果,然后过程却不是我真正想做的,在学校里压力不大,所以做事的时候不专心,总是很容易就进入了wandering的状态,结果当然不好。而另外一些事情,我做的时候很enjoy,虽然看不到什么结果,但积少成多最终还是会有结果。因此,在我的毅力得到充分的提升之前,或许我还是应该选择多做一些喜欢做的事情,而如果一件事情我发现无法强迫自己专心做下去那就应该放弃了。

其实人是否应该选择做一些自己不喜欢的事情,然后积累资源(例如金钱)再去做一些自己喜欢做的事情,这一直是个争论。有些成功人士认为这是应该的,但另一些则认为你必须从一开始就尽量选择自己真正喜欢的事情,那样才能把你的生命效率最大化,从而无论做出来是什么都是最成功的。Anyway,反正成功的定义是很personal的事情,你觉得自己活着怎样好就怎样好,没必要考虑太多社会评价,所以这个讨论上存在分歧也是很正常的。

2007年6月28日星期四

IQ 测试

今天早上的数字图像处理期末考试完全变成了IQ测试,非常有趣。作为专业选修课,很多人都是上课但不怎么听,书也不怎么看,然后就去考试了。考试是开卷的,说明了题目来自平时作业,不过数据会有所有不同,于是大家就把老师给的作业答案带去了。

这里先说明一件事,就是其实大多数人的作业也不是自己做的,都是参考着一份别人做好的“作业模板”做的,最好的情况下这份模板需要你填的就只剩下姓名和学号了。因此,考试的时候即使仅仅考作业题,也未必是懂的,那么怎么办呢?就只有临场学习咯。

IQ题是怎样的?例如3*3共9个图案,其中缺了一个,要你从4个里面选一个补上去让它最符合规律。这就是今天我们考试所做的事情。我们看着那份作业答案,里面只有题目和答案但没有中间过程,于是我们就要想尽办法去找规律——到底如何从题目推导出答案,然后模仿着这个推导过程去做试卷。整个过程真的和考IQ差不多,不过题目要难度了,因为规律并不简单。

2007年6月26日星期二

I love Microformat

在我制作自己的在线简历以及作品时,就想到是否能设计一种microformat用于描述resume。事实上,相信我们很多人都有过在网上投递简历的经验,每次大多数相同的项目都要重新填写,极之麻烦,所以我就希望这些重复工作能够一次搞定。相对于制作一个客户端的helper来帮忙填写简历,我更希望是一种描述简历用的microformat,因为我自己正在制作在线简历,而且希望这是一个有潜力的市场。

每天都有很多人网投,特别是毕业生,如果制作一个helper确实能够帮上忙的,然而这个工具无论被多少人使用,最终就是一个小软件,别人极容易模仿着做一个更好的。这样的客户端软件没有任何的粘连性,用户觉得另外一个更好的话随时可以换,所以做成服务很有必要。Web2.0时代,SaaS了,这才是真正的发展方向,而microformat能够和这个沾上边。

开头我们当然只有描述resume的一种microformat,制作在线简历的人少之又少,就好像回到了1998年之前,能够拥有一个美观而且有内容的个人网站是非常值得让人羡慕的事情。1999年,Blogger出现了,为当时那些拥有个人网站的精英群体实现了一个梦想——简单易用的日志管理方式。然而一开始确实也就只有高端群体在使用Blogger这样的工具,直到被称之为blogger年的2003年,使用各种blogging工具的人数开始激增。我期望这种描述resume的microformat的发展也如此,在经历了只有少数高端用户使用的阶段后,能够逐步大众化,变得好像blogging一样容易。

这背后潜在的市场也就是在线简历的空间与管理服务了。就如BSP一样,如果提供一定的存储空间,并且有相对简单易用的编辑功能,若干可以选择的模板,相信谁都可以马上制作一份在线简历,然后或许再购买一个域名搭配上。就如同Blogger提供的服务一样,中低端用户或许只会简单地从模板中选择一个合适的,并且将简历放在服务提供商的空间上,就好像Blogger发布到Blog*Spot上一样简单而且免费;高端用户会购买自己的域名和空间,将简历发布到自己的空间,或者关联到自己的域名,设置更加精美的模板,以便和自己原有的个人网站融为一体。

发展这个市场所面对的问题是,假如多遍填写简历已经够麻烦,为什么还要多维护一份简历,这份可是要保持更新的哦。这就是背后microformat所需要去推动的事情,我希望以后那些专门做网投的网站支持这种microformat,如果你有一份在线简历了就可以直接输入其地址,网投网站能够自动抓取你的简历页面并且完成信息提取工作,之后你就仅需要填写一些与当前申请工作有关的特性信息就行了。

令我感到吃惊的事情是,最近一次查阅microformats.org的时候发现已经存在一种基于hCardhResume格式了,虽然仍然在草案状态,支持的简历栏目也仅包括最基本的,然而却已经得到广泛的应用。已经有一些人在自己的在线简历上使用了hResume,也有专门生成hResume代码的工具,甚至连WordPress插件也已经有了。那么看来高端用户要采用hResume已经很容易了,只要他现在使用的是WordPress,马上就可以通过该插件在自己的个人网站上添加简历。

虽然我前面所说的潜在市场或许已经有充足的供应了,新竞争者要进入市场必须有相当的吸引力,然而我还是觉得能有microformat支持是一件很好的事情。microformat的最大好处是无需丢弃任何已经写好的HTML代码,需要的仅仅是添加新的附加信息上去而已,只要你觉得一种信息有对应的良好组织方式,你就可以提出一种microformat去描述它,以便更多的浏览器、搜索引擎更好的理解这类信息。

2007年6月22日星期五

Adobe Fireworks CS3 很容易上手哦

我以前一直不喜欢Macromedia的产品,因为觉得它们用起来都太像堆砌积木了。堆砌积木不正是编程的理想模式吗?低耦合度的话就是,然而Macromedia自动生成的代码却不是。

就拿DreamWeaver来说,如果一个页面的源代码一看就发现全是MM开头的JavaScript函数,那么我就不会看下去。使用DreamWeaver生成JavaScript是不对的,至少我认为是不对的。《程序员修炼之道》里面说明了你不应该用一个生成代码的向导,假如你无法理解它生成的代码是干什么的话。而用DreamWeaver的人很多都在干着事情,感谢51js之类的网站,以及非常丰富的DreamWeaver脚本生成扩展,中国有大量的站长在使用那些他自己完全不知道在干什么的JavaScript。

然而堆砌积木也有好处的,特别是对于非专业人员。例如我不是专业的平面设计人员,我无法驾驭Illustrator这样的东西,这时候简单堆砌图形的Fireworks就非常适合我了。我主要使用Fireworks来设计页面的mockup,设计好就slice几下把必要的图片输出,接着就可以在Expression Web Design里面写XHTML+CSS了。Fireworks将矩形、圆形等常用的简单构图元素突出出来,而页面设计需要的正是大量基于矩形的元素,因此使用Fireworks设计mockup的效率真的很高。

2007年6月21日星期四

Becoming More Spiritual

人生总有突然为某些事情顿悟的若干次,我不知道这次算不算,但我发现自己更加spiritual了。

关于如何确定人生目标的方法看过一些,不过我觉得没必要试,反正我是一个很清楚自己想要什么的人,不过现在发现那些我很清楚的人生目标都是比较material或者低层次的spiritual的。李开复的那本《做最好的自己》中提到确定人生目标的时候,提到了假如你剩余若干时间的生命的时候会做什么,以及你死了之后别人的评价会是如何。对于前一个问题,我还算清楚,其实我不觉得有什么很急迫的事情必须完成的,所以我会放松地去完成我觉得可以完成的事情,就算完成不了也没什么所谓。至于后面那个问题,在高中的时候我对它的想法是比较material而且tragedy的。

我之前认为,即使我拥有一家庞大的国际化企业,有一个健康的家庭,我死掉之后事情将被很sophisticated地处理掉——幻想一下我有个office,而且至少是在中信那样能称之为tower的建筑物的顶层,然后有一天我就从那个office跳下去了,这一跳纯粹是“好奇心能够杀死一只猫”的结果,也就是我想知道之后会发生什么事,假如我还有能力知道的话。在大型城市中,救护人员会快速赶到现场,我的意思是帮忙确定我的死亡,好让那些记者回去有东西可写。然而在记者把文章发表出来之前,董事会的各位已经在思考自己的下一步棋怎么走了,因为接下来可能有不少的因素会显著影响股价,持住一大堆股票的人当然要先想清楚自己该买还是该卖,然后选择正确的对外立场。与此同时,企业良好的管理机制开始运作,适当的人选自然会被放到适当的位置,用来弥补缺少我的作用力以后所造成的影响,生态环境过一段时间自然能够重新平衡。至于家庭,他们——也就是妻子和孩子——是否好像企业一样找个人来替补我的空缺就是他们的事情了,反正我也没有发言权,然而只要他们有相当的股票在手,并且上面的事情能够按照预期的平稳处理掉,那么他们的生活也能平稳过渡。总之,这些如此sophisticated的事情总有人能负责的,无论我是否希望有人负责。

反正呢,material的东西你是无法带走的,而这个社会现在已经能够非常高效地把你遗留下来的东西瓜分干净,之后也或许就不会有人想要去悼念你。因为说到material了,如果你真的有相当多的股票,即使你的所有器官都捐赠出来,和这些股票的价值相比也就是冰山一角,除非你能在股票上印上你的头像吧,否则这个注重效益最大化的社会真的没什么必要去惦记你。因此,你留下的material价值越多,你的身体或者是贴身物品的价值比例就越低,别人就越没有理由去想着这仅占一点点比例的价值,这就是所谓的tragedy了。

现在,我突然间感觉到spiritual的重要性了,因为这才是你真正能够带走的东西。你死掉后别人没办法从你的大脑里把你的思想挖出来瓜分掉,至少暂时没办法吧,那么人们就会真心地感觉到他们失去了你。如果你的spiritual有相当的价值,例如你是画家或者诗人之类的,那么这个社会就会感觉到它真的失去了巨大的财富了。因此,在考虑人生目标的时候,spiritual是一个很重要的衡量角度,忽视了这个角度就可能导致不那么理想的结局,我的意思是假如你在乎别人对你的评价,又或者你死掉以后还有能力去评价结局是否理想的话。

另外最近看到一篇名为Life After Death的文章,如果你之前有看过我那篇《小睡 / Polyphasic Sleep》的帖子,或者你有关注过“小睡”的概念,就肯定知道其作者Steve Pavlina。这家伙的文章貌似挺注重与生活质量的方面,例如之前介绍过小睡,最近又在写如何能做到早起床;同时也有关注精神领域的题材,例如如何发现你的人生目标。我们把话题回归Life After Death这篇文章上,它的中文翻译版本名为《死后的生活》,主要讲你死掉后可能的精神状态。对于死后的生活来说,最不幸的,或者对某些人来说是最幸运的,也就是你的精神和肉体一起byebye了,那么你也就没办法为死亡做什么准备了,好好享受现有的生活吧;另外一种情况就是你的精神仍然存在,或许被冻结了,或许能继续发展,无论是哪种情况,假如你在死掉之前积累了充分的spiritual,那都肯定是好事情。

因此,我开始感觉到积累自己spiritual方面的感悟是生活中总要的一部分,必须投放相当的时间与精力去修炼,以便将来无论发生什么事情都至少能spiritual的生存下去。最坏的情况下,不是直接死亡,而是被关进纳粹集中营那样肉体上名存实亡,有相当的spiritual生存能力或许还能让你的肉体坚持下去。

2007年6月18日星期一

从 Dynamic Data Control 回归普通的 Data Control

我尝试在自己的软件工程项目中使用DDC,希望它能带来一些比较敏捷的特性,虽然在那么正统的软件工程课程上搞敏捷貌似有些不妥,不过其实也没有几个小组是完全按照流程来做的,反正最终所有文档齐全了演示也能让老师觉得效果不错那就行了。事实上很多小组都是随便分析一下就开始写代码,东西做出来了再补文档,哈哈……

新建了一个ASP.NET Futures的网站后,我先尝试使用DynamicDataAuto控件,发现确实如demo显示的那样“功能齐全”——所有字段都能在GridView中列出来,支持图片的直接显示,也支持外键的直接链接,一个页面上集成了所有的CRUD操作。然后我开始想细致定义这个页面,发现这个scaffold不能好像RoR的scaffold那样自动分解为低级代码,于是就尝试手工转换。首先把DyanamicDataAuto删掉,按照我的需要换上特定的Dynamic Data Control,然后发现可定制性不强,继续换……最终,页面上的控件降级到GridView了,Dynamic Data Page的特性完全没有用上,于是干脆换回普通的Page。

实际上现在的Dynamic Data Control一点都不好用,就如同ASP.NET AJAX的拖放支持一样——现成做好的控件限制太多,自定义需要实现的也太多,难以快速开发一个支持拖放特性的自定义控件。如果要使用Dynamic Data Page,那意味着Page本身为你提供了统一的Data Source,所以Page上面的Dynamic Data Control就不用设置Data Source了,然而这个特性并不能为传统Data Control所用,这就意味着一个Page上要么只用Dynamic Data Control要么只用传统Data Control。然而Dynamic Data Control的定制性实在有限,所以面对上述binary choice只能选择传统Data Control了。

暂时来说,我们也不能用Dynamic Data Control做些什么,只能等下一个版本的ASP.NET Futures看看是否有什么改善。现在的Dynamic Data Control就如同Atlas的第一个CTP那样(那时候还有<atlas:TextBox />这玩意儿),或许到最后整个模式都改掉了。

2007年6月13日星期三

慎用 XHTML 标签的自关闭写法

我们都知道XHTML里面的img标记应该这样写:<img alt="" src="" />,这种写法也就是所谓的自关闭,在XML中是完全合法的写法。如果你熟悉XML相关的开发,可能也就习惯于这种写法,想着XML中任何不含子节点的元素都可以这样写,那么XHTML中没有内容的标签也都可以这样写。XHTML中理论上当然允许任何标签以自关闭的方法来书写,然而浏览器兼容性却带来了新问题,那就是IE无法正确识别某些标签的自关闭写法。

请尝试输入以下XHTML代码并在IE中浏览:<p>hello <script type="text/javascript" /> world</p>,你会发现只能看到前面的hello而不见后面的world,这事情让人挺无法解释的吧。可能有不少人都曾经遇到过这个问题,并且花了几个小时在上面都找不到合理的解释。

解释源自另外一段类似的代码:<p>hello <textarea /> world</p>,你在IE中看看其显示效果,能够得到合理的解释了吗?我们能够看到前面的hello正常显示了,而后面的world则显示在textarea里面,这证明IE并没有正确识别textarea标签已经自关闭了,而是当它没有关闭,并将后面的内容识别为textarea内部的内容。

这时候我们就明白前面那段代码为什么看不到后面的world了,因为它被当作script的一部分来识别了。这就说明了,在我们使用XHTML时并不能好像XML那样随意的使用自关闭的写法,只有少数原本不需要关闭的标签可以用自关闭的写法,其他标签即使没有任何内容最好也用成对的关闭写法。

最后需要提醒大家的是,其实弱智的parser不仅仅IE有,很多地方都可能碰到由于parser不严谨而引起的问题,所以我们在书写XHTML的时候还是要迁就一些老HTML继承下来的习惯,不能好像真的XML那样自以为符合标准了就随意写。不信?那么再试一个吧:<p>hello <br></br> world</p>,留意IE与Opera中的显示效果。

Update: 有部分读者认为我举的例子是不符合XHTML规范的,那么请先阅读XHTML规范Empty Elements一节的中文翻译如下:“空元素必须要么有一个结束标记,要么以/>结束,例如<br/>或<hr></hr>。请参考HTML兼容性标准以获取关于确保向后兼容HTML4浏览器的信息。”可以看得到,规范中也给出了<hr></hr>这样的例子,说明<br></br>的写法是符合XHTML规范的,只是没有兼容HTML4标准。那么到底XHTML是否兼容HTML4呢?我们来看Compatibility Issues一节,中文翻译如下:“虽然并没有要求XHTML1.0文档兼容现有的浏览器,但在实践中这并不难做到。”因此,XHTML是没有规定文档必须向下兼容,我给出的例子都是合法的XHTML文档片断,当出现在完整的XHTML里面时也全部能通过W3C Markup Validation Service的验证。

Update again: 其实我写这篇文章的目的不是为了强调只符合XHTML规范就行了,也不是强调符合XHTML同时兼容HTML4就够了,而是应该考虑更多需要兼容的情况。例如你的CMS中允许用户提交HTML,提交的HTML经过SgmlReader或者其他方法格式化为XHTML,同时或许还做了其它XML处理,这时候就有可能将用户提交的<textarea></textarea>转换为<textarea />,这种情况下你需要通过跟踪调试找出问题并不容易,因为XML处理并没有违反任何规范,每一步的处理都是符合语义的。另外最好不要把<br />写成<br/>,因为确实有些弱智的parser仅仅因为少了一个空格就无法正确识别。

2007年6月12日星期二

探索 ASP.NET Futures (Part 3 - Client Diagnostics)

貌似ASP.NET 2.0新增的诊断相关服务没多少人关注,更没多少人用,不过对于正在使用此类服务分析站点的人来说,肯定非常期望ASP.NET AJAX中的客户端代码错误也能记录到诊断日志中,这样就能获取更丰富的数据来分析站点不稳定因素的来源。

ASP.NET Futures中已经引入了客户端诊断服务,在解释此服务之前不妨先思考一下假如你要自己写一个客户端诊断服务会怎么做。如果我来写一个客户端诊断服务,我会设计一个Web Service负责收集错误信息,然后记录到数据库中,然后写一个js负责拦截客户端异常并且发送到该Web Service。ASP.NET Futures (May CTP)中引入的客户端诊断服务也差不多就这样子,只不过设计得更加通用更具扩展性。

使用客户端诊断时你首先要在页面上放置一个Diagnostics控件,它负责的就是向客户段输出一段js以便拦截客户端异常。熟悉ASP.NET AJAX控件设计的人应该能猜到Diagnostics控件肯定是一个ScriptControl,不熟悉ASP.NET AJAX控件设计的话也可以通过这个例子了解到,如果你有一个客户端组件要以控件方式在Page上部署那就应该考虑继承自IScriptControl接口了。

接下来我们要部署接收诊断信息的web service了。新建一个名为DiagnosticsService.asmx的web service,更改其code-behind声明如下:
<%@ WebService Language="C#" Class="Microsoft.Web.Preview.Diagnostics.DiagnosticsService,Microsoft.Web.Preview" %>
这样做的意思是,此web service不是使用你写的code-behind代码,它的code-behind类直接使用上述的DiagnosticsService类,所需的函数都在里面了,你不需要再写任何代码。那么我们到底在哪写代码记录客户段发送来的诊断信息?先别急,接下来我会解释此事。需要说明的是,ASP.NET Futures现在这个做法的风格和原来的ASP.NET AJAX有点格格不入,因为它没有直接采用HttpHandler的方式直接暴露此web service,而必须要开发者自己部属一个asmx文件,将来此做法可能改变,届时将不再需要手动部署asmx文件。

仔细留意DiagnosticsService,你会发现它有一个OnClientException的事件,这才是你真正需要关注的地方。为了确保将所有客户端异常都记录下来,你可以在Global.asax的Application_Start中绑定此事件,在HttpModule中绑定也是可以的。在事件的处理函数中,你将通过EventArgs获取到一个ExceptionServerInfo的类型,在里面将会记录如下一些与客户端异常有关的属性:

  • Data
  • FileName
  • LineNumber
  • Message
  • IpAddress
  • Timestamp
  • UserAgent

你可以直接访问这些属性,以你喜欢的方式进行日志记录,例如记录到你自己的数据库,或者是ASP.NET 2.0的health monitor。

好了,这样就解释完如何使用客户端诊断服务了。嗯……等等……还要一样重要的事情没有说,那就是在web.config中启用此项服务。首先要声明config section:
<configSections>
  <sectionGroup name="microsoft.web.preview" type="Microsoft.Web.Preview.Configuration.PreviewSectionGroup, Microsoft.Web.Preview">
    <section name="diagnostics" type="Microsoft.Web.Preview.Configuration.DiagnosticsSection, Microsoft.Web.Preview"/>
  </sectionGroup>
</configSections>

然后声明启用此服务:
<microsoft.web.preview>
  <diagnostics enabled="true"/>
</microsoft.web.preview>

最后,如果你有兴趣继续关注有关ASP.NET Futures新功能的介绍,可以考虑订阅我的博客:

2007年6月11日星期一

Apple 发布 Safari 的 Windows 版了

以前一直因为没有Windows平台上的Safari,所以设计CSS Template的时候就没办法测试对Safari的兼容性,或者只能使用browsershots.org这样的服务来查看CSS Template在Safari中的视觉效果。现在Apple最新发布的Safari 3 Public Beta已经有Windows版本了,那就能很方便的测试CSS Template的兼容性了。

Safari的Windows版本有一个重要的战略意义,那就是让你可以测试你的应用是否能在iPhone上面跑起来,包括AJAX应用。由于iPhone暂时还不会有任何第三方软件,所以它上面能够有什么第三方服务就依赖于Web了,而Safari对AJAX的支持则成为了亮点。如果你是服务提供商,并且希望你的应用将来能被iPhone用户所使用,那么使用PC上的Safari将能让你提早进入网站兼容性测试的环节。

探索 ASP.NET Futures (Part 2 - Search Enabled)

在本系列的上一篇文章中,我们探索了ASP.NET Futures (May CTP)的SearchSiteMap功能,说明了如何将ASP.NET的SiteMap影射为符合Sitemaps协议的XML以便搜索引擎更好的抓取我们的站点。然而让搜索引擎更好的抓取我们的站点了,这部分的优化却仅仅对来自于搜索引擎的访客有用,这是否有点浪费?我们是否可以选择站内的搜索也通过Internet搜索引擎(例如Google)来实现,从而避免开发自己的站内搜索引擎?从ASP.NET Futures (May CTP)开始,这也便得可能了,需要使用的仅仅是SearchDataSource控件以及简单的几句配置。

首先说说SearchDataSource是干什么的吧,它其实是一个ObjectDataSource的派生类,只不过设置了基类的TypeName为"Microsoft.Web.Preview.Search.SearchService",而SelectMethod则是"Search"。事实上你放置一个普通的ObjectDataSource然后设置这两个属性也就可以获得SearchDataSource的效果。之后你就可以像使用普通DataSource控件那样使用搜索返回的数据,绑定到Repeater或者GridView,又或者绑定到你自己的数据控件都是可以的。

SearchDataSource返回的貌似是一个IEnumerable,而事实上是一个List<SearchResult>,通过Reflector阅读SearchService.Search方法的代码就知道了。然后我们来看看SearchResult类型包含什么属性。它拥有3个string属性,分别是:Url、Title、Description。在使用SearchDataSource的时候,数据控件绑定这3个属性就够了。那么之后我们还需要做什么呢?接下来就是配置provider了,因为SearchService.Search方法正是通过轮询各SearchProviderBase派生类来获取搜索结果的。

这听上去很复杂的样子哦,远超过了放置一个SearchDataSource的难度。其实不是的,ASP.NET Futures (May CTP)中已经包含一个SearchProviderBase派生类了,那就是WindowsLiveSearchProvider。此provider的配置方法如下:
<microsoft.web.preview>
  <search enabled="true">
    <providers>
      <add name="WindowsLiveSearchProvider"
        type="Microsoft.Web.Preview.Search.WindowsLiveSearchProvider, Microsoft.Web.Preview"
        appID=""
        siteDomainName="" />
    </providers>
  </search>
</microsoft.web.preview>

其中的appID是你在Windows Live Search申请的Application ID,还没有Application ID的话可以到Windows Live Search的Developer站点申请一个。siteDomainName就是你的网站的域名,例如我的网站就是CatChen.biz。另外,你还要确认Windows Live Search已经抓取了你的网站的页面,通过大部分搜索引擎都支持的site:指令你就可以知道一个搜索引擎对你的网站的抓取情况,例如我可以通过site:CatChen.biz搜索来了解Windows Live Search对我的网站的抓取情况。完整配置并且确保搜索引擎对你的站点的抓取后,你就可以通过SearchDataSource获得期望的搜索结果了。

如果你不满足于WindowsLiveSearchProvider提供的功能,你可以实现自己的SearchProviderBase派生类。在官方文档中Enabling Search页上,最后一个例子提供了两个自定义的provider,一个是用于Yahoo的,另一个是用于Index Server的。另外不要把目光局限于某个特定的搜索引擎,如果你觉得Google、Yahoo、Windows Live都不够好,那么你可以考虑做一个自己的meta-search(源搜索)引擎。所谓的meta-search是指通过调用若干个不同的搜索引擎,根据它们的返回结果再次进行信息的分析与提取,最后生成自己的搜索结果。

在你实现了自己的SearchProviderBase派生类后,有一点需要注意的,那就是SearchService是不知道返回结果的排名的。一般我们都会认为搜索结果越靠前的,当然应该是越匹配的,这在配置一个provider时是没什么问题的。然而配置多个provider后,SearchService仅仅是将多个provider的结果拼接成一个更大的List<SearchResult>,这时候就毫无排序而言了。因此,如果你需要使用多个provider,我还是建议直接写一个SearchProviderBase派生类,使用meta-search的方式自己去合并以及重排结果。

最后,如果你有兴趣继续关注有关ASP.NET Futures新功能的介绍,可以考虑订阅我的博客:

2007年6月7日星期四

Languages Differentiate Thinking?

其实很早的时候我就意识到可能使用中文思考相对于使用英文思考来说,对思考者某些特定领域的思维能力会带来一定的正面或负面影响,不过我不很确定有什么例子能论证这个话题,也不知道哪个领域的思维能力造成的影响可能是最明显的,所以也就没有继续想它。

上个星期新东方有个老师说,使用英语思考能够让人的逻辑思维能力更加严谨,因为没有哪一个在中国的华人能够拿到诺贝尔奖,然而华人在美国改用英语思考就能拿到诺贝尔奖了。或者这根本不是一个有价值的例证,反正新东方老师上课吹水是必须的,然而这却让我重新感到问题本身确实值得思考。

首先我们来看看程序设计语言而不是自然语言之间的区别。我最近阅读到一篇文章讨论了程序设计语言与解决方案之间的关系:On Semantic Distance and Computer Languages。文章中认为,使用的程序设计语言的语义和解决方案的距离越近,实现起来就越容易,因为所谓的实现就是搭桥跨越程序设计语言以及描述解决方案的语言。然而文章最后说道,世界上有5000种语言(包括方言),你是不可能找到一个等价语义集合的,因此也就不存在一种语言总是最贴近任何一个解决方案,那意味着也不可能存在所谓的最好的程序设计语言。

可能这样说比较空泛,那么我们通过一些实际的例子来说明计算机语言与描述解决方案的语言之前的距离为何可以相去甚远,当然后者暂时指代的就是英语为主的描述性语言。假如你需要取一个值,它代表当前时间的20分钟前的时间,用Java写的话代码如下:
new Date(new Date().getTime() - 20 * 60 * 1000)
然而用Ruby写的话代码如下:
20.minutes.ago
上述例子来自Sometimes less is more,阅读该篇文章你可以看到更多这种Java和Ruby的比较。在此我无心说Java与Ruby之间哪个更好,我只是想找一个例子证明不同语言与解决方案语言间的距离是可以有很大差别的。

解释完程序设计语言有关的事情,那么我们可否直接拿上例来推导,并由此得出一个类似的结论,那就是不存在一种自然语言总是最贴近任何一种思维模式。正如如果你想写一段代码获取代表20分钟前的时间值有两种思维方式以分别适应Java与Ruby一样,中文与英文也有描述一件事情的两种不同思维方式,而随着你使用特定一种语言的时间积累,这种方式将反过来对你的某些领域的思维能力造成影响。

然而到底中文与英文训练人的思维能力的结果如何呢?我也不敢乱下什么结论,但从表象上看来,貌似英文适合逻辑推理而中文适合艺术创作。然而到底事实是如何的,我们可否遮盖其他文化因素的影响单纯探讨使用一种特定语言对人的思维能力的影响,这就是我能力范围之外的事情了,应该由社会学家以及语言学家来解决。

2007年6月3日星期日

Broaden Choices before Making Decision

继续发展《还有新鲜感吗?》那次的话题……最近真的在考虑MacBook哦,漂亮的MacOS为什么不能成为选择呢,反正Vista也是在模仿它的。

其实在有充足的选项之前,干什么那么早选定一个固定的方向呢,提前缩窄搜索范围,这在遗传算法里面叫做“早熟”哦。这个所谓的“早熟”会导致之后都搜索都限制在这个小范围内,即使让你求得了这个小范围内最优解,在全局范围内也不一定怎么优。所以遗传算法通常要避免“早熟”,在开始的时候要确保搜索解集尽可能在全局里分散。当然,还有一些更高级的方法避免类聚,然而这部在我们的讨论范围之内。

另外一种要防止出现的情况是不收敛,或者叫做“迟熟”,假如你一定要找一个和“早熟”对应的说法的话。不收敛意味着你在一个很广泛的范围内搜索来搜索去,貌似搜索了很多的解,但是你所能得到的最优解看起来总合全局最优解有一定的距离。

不过对我现在的情况而言,应该控制好避免“早熟”,过早收敛是不好的。找mm这事情也一样,如果不想“早熟”或者“迟熟”就应该做好范围控制,不能让它过早收敛,但又要避免不收敛的情况。

2007年6月2日星期六

Google 的赞助商链接在搜索结果之上!

Google向来声称广告在右边,明确标出,不会打扰用户看搜索结果,搜索结果放的位置才使人视觉焦点的主要位置。只有相关度本身就非常高的广告能够出现在搜索结果之上,并且也会标明为广告。

这个声明在PC上是成立的,即使把CSS给禁用掉仍然成立。CSS禁用掉不是两栏布局就变成一栏了吗?哦……别就只知道CSS布局,我们还有原始的table布局呢,Google的搜索结果页就通过使用table确保广告再右侧。

那么什么情况下广告会跑到上面了呢?在用Opera Mini的时候,因为Opera的服务器会对页面进行优化后再发送给客户端的Opera Mini进行呈现,这过程中不知道什么优化规则就让广告显示到前面了。不过先说明,这样的结果是必然的,因为在HTML中广告出现在搜索结果之前,Opera对内容按顺序进行分段并且优化时先处理广告自然先输出广告。

我觉得这种特殊情况对于Google来说是非常讽刺的,Google一直大力宣称搜索结果上层的位置你无法买到,然而在Opera Mini上显示时显然就是买的结果先出现。并且因为Opera Mini上面能显示的字体只有一种,所以“赞助商链接”这几个字就不那么明显,不留意看你就以为第一条广告是第一条结果。即使你知道那些使广告,要寻觅哪条才是第一条结果也许要费些时间仔细看。

2007年5月30日星期三

Microsoft Surface 发布啦!

非常cool的Microsoft Surface技术终于发布了,之前在YouTube上看过一些demo显示这是Microsoft正在研究的技术,以为要多少年之后才能变为实用,因为实在太Sci-Fi了。

值得注意的是,Microsoft Surface官方网站演示中所用到的UI技术正是WPF以及其基于的.NET Framework,这在ScottGu的blog中有所提及

另外,Microsoft考虑优先将这台售价五千到一万美元的机器卖给商业合作伙伴,例如高档的餐厅、赌场、影院等,并且可能将于2007年底就开始投入使用,所以这东西是一点都不Sci-Fi的。

Popular Mechanics有另外一段视频深入解释了一些技术细节,可以在这篇名为Microsoft Surface: Behind-the-Scenes First Look的文章里看到。

Less Coke and Chocolate

昨天去补了很多只牙,估计都是可乐和朱古力惹的祸。平时经常吃完甜食,没有清理口腔的习惯,所以糖分就留在牙上面导致蛀牙了,而且这次还蛀得很深了,哎……

另外感觉省口腔医院很废,看上去设施和服务态度一流,不过总是治不好就是了。大半年前我已经开始有一点牙肉痛了,但中间去过几次省口腔医院他们都没告诉我有蛀牙,搞到拖了那么久。

2007年5月28日星期一

探索 ASP.NET Futures (Part 1 - Search & Sitemaps)

如果你在使用ASP.NET站点,同时又希望它Search Engine Friendly一些,很可能你就希望它有一个Sitemaps。在这里我们说的不是ASP.NET的SiteMap,而是Sitemaps.org定义的基于XML的Sitemaps协议,注意这两个名字的大小写以及单复数,之后我都会这样区分它们。Sitemaps协议有点类似RSS或者Atom,只不过它描述的不是最近的内容更新,而是整个站点的地图,主要用来描述特定URL的重要程度、更新时间及频率等。搜索引擎如Google是支持Sitemaps的,通过Google Webmaster Tools(以前叫做Google Sitemaps)你可以提交你的站点的Sitemaps,以便Google更好地索引你的网站。

简单调用

在ASP.NET Futures (May CTP)之前,如果你想要为你的ASP.NET站点增加Sitemaps支持,恐怕必须自己实现一个特殊的页面(或者HttpHandler)用于读取ASP.NET SiteMap并输出为Sitemaps协议。而现在这工作可以交给ASP.NET Futures的AspNetSiteMapSearchSiteMapProvider来做了,你需要做的仅仅是在web.config中写上几句。由于这个功能属于ASP.NET Futures中SearchSiteMap这个类别,所以需要在web.config中对该节进行配置:
<microsoft.web.preview>
  <searchSiteMap enabled="true">
    <providers>
      <add name="Navigation" type="Microsoft.Web.Preview.Search.AspNetSiteMapSearchSiteMapProvider, Microsoft.Web.Preview"/>
    </providers>
  </searchSiteMap>
</microsoft.web.preview>

在这个配置里面,我们启用了SearchSiteMap,然后配置了一个名为"Navigation"的Provider,此Provider使用AspNetSiteMapSearchSiteMapProvider类,就这么简单,和配置任何其他Provider的形式完全一致。之后你还需要确保一下有关的HttpHandler配置好了,如果你新建网站时使用的模板是ASP.NET Futures的,那么HttpHandler就应该配置好的了,配置信息如下:
<add verb="*" path="SearchSiteMaps.axd" type="Microsoft.Web.Preview.Search.SearchSiteMapHandler" validate="True"/>

这时候,如果你的网站已经正常启用ASP.NET自身的SiteMap功能,例如使用静态的Web.sitemap,那么访问SearchSiteMap.axd就应该能看到按照Sitemaps协议输出的结果。这时候或许你会很奇怪,为什么结果只有一条记录呢?这就是Sitemaps的递归调用了,这个主Sitemaps仅仅声名了我们之前配置的那个名为"Navigation"的Sitemaps的地址,也就是SearchSiteMaps.axd?sitemap=Navigation。打开这个地址,你会发现仍然是一个Sitemaps,它里面包含的就是ASP.NET SiteMap提供的数据了。

深入看看

接下来,我们用Reflector来看看Microsoft.Web.Preview.Search下面的一些类的实现方式。我不准备详细分析代码了,因为代码都很简单,直接说说看完的结果吧。如果你之前浏览根据SiteMap生成的Sitemaps时发现少了些东西,在这里你就知道如何把这些项目补充上去了。Sitemaps协议中关于一个URL能够包括以下几样信息:

  • 地址:也就是URL本身
  • 最后更新时间
  • 更新频率:此URL的内容多久更新一次
  • 重要程度:一个0到1的值,默认值为0.5,搜索引擎并不一定根据这个值来判断URL的真正重要程度

然而自动生成的Sitemaps仅仅包括前两项信息,如果我们需要后两项信息就需要手动增加。怎样手动增加呢?因为SiteMapNode类似于字典,能够访问this [string key],所以只要SiteMapNode[]存在"lastModified"/"changeFrequency"/"priority"这几个值就能自动输出到Sitemaps中,而且"lastModified"会覆盖对应Page的aspx文件的真实最后更新时间。

简单举例说明这功能怎么用,假设你使用的是静态的Web.sitemap,我们已经习惯这样定义一个SiteMapNode:
<siteMapNode url="Default.aspx" title="Welcome" description="" />

而增加特定的属性只需要这样定义:
<siteMapNode url="Default.aspx" title="Welcome" description="" changeFrequency="daily" priority="0.8" />

支持Dynamic Data

上面说了那么多,也就仅仅能做到支持系统自带的SiteMap,而实际上SearchSiteMap还能够对Dynamic Data提供特殊的支持。Dynamic Data简单易用,好像Ruby on Rails那样支持scaffolding,预览了ASP.NET将来在敏捷方面的发展。有关Dynamic Data Control的详细信息,请参考Dflying的文章,我们这里仅讨论SearchSiteMap的支持:

需要支持Dynamic Data的话,首先你要实现自己的DynamicDataSearchSiteMapProvider。大家不要一看到要继承自系统类实现自己的类就觉得是非常复杂的事情,其实这里我们仅需要override掉一个函数,也就是DynamicDataSearchSiteMapProvider.DataQuery()。在这个函数中,我们需要返回一个IEnumerable,其中的元素需要具有主键列名属性以及以下属性:

  • SiteMapLastModified
  • SiteMapChangeFrequency
  • SiteMapPriority

你很可能会问,为什么要是不确定类型的IEnumerable而不是确定类型的List<>呢?想想.NET Framework的什么部分用IEnumerable用得最多吧,那就是LINQ。如果你在QueryData()中直接使用LINQ来筛选数据,你就不需要创建自定义类型并且自己填充IEnumerable了。况且,主键列名也不是确定的,如果用一个属性记录其名称用另外一个属性记录其值那就很麻烦了,所以ASP.NET Futures选择了上述充分发挥LINQ优势的做法。

最后,我个人感觉SearchSiteMapProviderBase的设计有点问题,它作为AspNetSiteMapSearchSiteMapProvider与DynamicDataSearchSiteMapProvider的基类,其中包括QueryData()方法,然而此方法只有DynamicDataSearchSiteMapProvider用到,很显然就应该将它放置到DynamicDataSearchSiteMapProvider里面。

2007年5月24日星期四

被急 call 去参加 Google 面试

早上起来后,本来是计划帮一位老师把他的个人网站的模板设计好的,10:30突然收到电话通知叫我去面试。

开头我说大学城出市区起码一个多小时,很难赶上,但对方没有改时间的意思,说“那就只能另外约时间了”。这时候我当然不会选择让电话挂掉然后等下一次通知啦,所以马上问她下午是否可以,这时候才知道原来下午的时间都排满了,才一定要我上午去,然后我就答应上午赶过去咯,她说超过12:00到也没问题。

跑到大学城北部枢纽的时候,发现刚刚走了一部3线,而且最近的司机都很守规则离站了就不开门,所以想着可能还要很久才开下一班车,结果发现10分钟左右就有下一部3线准备开出了。出市区用了我半个小时,然后打的去东方宾馆又用了半个小时,去到刚刚过了12:00。

至于面试,就是签了NDA再进行的,所以就没什么好说的啦,自我感觉不怎么样,没有很突出的表现。之前听说Google的面试都是一个早上几轮面试一起来的,如果你被通知可以走了然后就没消息了,那么你也不知道是在哪一轮面试被刷下来的。不过今天我面试完临走时问了一下,才知道过了的话会有二面通知,希望自己能顺利提供第一轮面试吧。

2007年5月22日星期二

Google SE 笔试通不过

之前浪费了那么多时间wandering,该报应的时间终于来了,Google SE笔试通不过,收不到面试通知。总结出来就两点:

  1. 改拓展与利用人脉的时候一定要利用好。人脉一方面要拓展,另一方面更要利用,就如钱一样花得值了的才是你真正得到的。其实早些时候就有人大家帮我做Google的推荐,当时一心想着去申ID,就不怎么重视SE笔试,也就没在这之前作推荐,笔势完才发现原来身边那么多人是找人做了推荐的,也就是说他们来参加笔试只是玩下,有推荐可以直接进入面试的。
  2. 该做好的事情一定要全方面重视。其实对于SE笔试我也是很认真的,提前咨询了别的学校举行笔试的方式等等,然而却疏忽了推荐着一个重要的项目。

Move on. Next Target. 继续努力,由于最近对Product Manager的职位比较花痴,所以决定目标是早日有足够的能力获得一个PM职位。

Keywords: cnblogs, profit, future and more

上个星期六下午参加完博客园广州的聚会,讨论议题是博客园的盈利与发展,那么我也来说说这事情。

您为什么愿意使用博客园

要探讨这件事,我觉得首要问题应该是“您为什么愿意使用博客园”,这个问题决定了博客园当前的价值曲线何在,然后你才能确定博客园是否应该迁移到新的价值曲线上面去。如果你问我为什么愿意使用博客园的话,我的回答会很简单:因为用它来写blog简单易用咯。

看到这个答案,你是不是已经笑到肚子痛了?好,那么你先捂着肚子,回想1999年Blogger刚刚创立时候的样子。那时候还没有免费的Blog*Spot,如果你是Blogger的用户你必须有自己的空间,而且要是支持FTP上传的,Blogger所负责的仅仅是根据你设计的HTML模板套用你保存在他们数据库中的数据,然后生成静态HTML并且上传到你的FTP。免费的域名和空间?没有,你自己想办法买去。漂亮的模板?没有,除非你自己精通设计与HTML。看到这里我想你的痛症已经从腹部转移到头部了,这么辛苦才能弄一个blog难道不是一件非常头痛的事情?

我们再来看一个例子,VeryCD与传统的ed2k network有什么不同?理论上整个ed2k network是连通的,资源是完全共享的,当然有些客户端mod设计为同类mod之间优先传输,不过我们暂时忽略掉这类情况。VeryCD的成功就在于它让资源下载变成了非常便利的事情,甚至不仅仅是资源下载,而应该说资源的消费(consume),包括搜索、预览、下载、使用全过程。很早很早之前就在Dash的blog看到一张brain storm的图,画着用户现在消费VeryCD资源时都会遇到什么障碍,其中包括例如播放DVDRip需要安装codec和字幕插件,不是专业用户不知道什么是codec更不知道codec存在版本冲突等问题。之后VeryCD是有致力于解决这些问题的,例如通过制作Storm Player来方便用户播放DVDRip,并且保证此播放器是无捆绑的,这就让用户摆脱了既想装codec又害怕惹上捆绑软件的困境。

总结一下,是否简单易用对产品能否进入大众市场起到关键作用。相信博客园不是想做1999年的Blogger那样的高端用户服务平台,所以易用性就成为了关键。星期六的讨论有人说到了开源项目,有人说到了出书,这些好主意最终能否让大众受惠也就看是否便利(convenience)了。

什么是便利

那么到底便利是什么?我们拿开源项目来说说吧。如果一个开源项目必须要你下载源代码后自行配置和编译,你会不会用?如果好像Community Server那样下载后就是一个Installer可以直接安装使用,你又会不会用?无论对于开源项目的开发者还是使用者来说,是否便利都是很重要的一个因素。例如我想参与到博客园主持的一个开源项目吧,我该如何做呢?先装个SVN吧,这谁不知道,说起来可真简单。但对于没经验的人来说,选择什么SVN客户端,装完之后如何用,这就是个大问题。先说说题外话,知道什么方法能够最有效增加你的feed订阅量吗?那就是在订阅链接旁边加上一个帮助链接,用户打开后能看到一篇文章详细解释什么是feed以及如何使用feed订阅。因此,如果想要有更多人参与到开源项目中来,第一件事应该是普及SVN的使用方法,这就减少了用户自己摸索恰当的SVN使用方式所需要的时间成本。

事实上,我觉得4C理论可以合并为3C,因为convenience也就是cost,不过不是你的cost,而是你的服务对象的cost。星期六讨论时大家的思维方向都是如何能让博客园的写作者能够从写作本身受惠,从而增加粘滞度以及吸引新人。实际上我们可以尝试反过来思考,就是如何降低写作者的成本,从而达到同样的效果。这种成本可以是任何东西,而不仅限于便利性。而且这种策略还可以推广到写作者以外的群体,例如读者、广告商,甚至其他潜在客户。

价值与成本的问题

服务业本身就不在于创造实物商品,而在于创造服务,如果你多观察美国的Web2.0网站创业模式,你就会发现他们有时候会用到一种思维——“如果我能够帮你减少一百万的成本,你肯定乐意从这一百万中分一部分给我”。然而中国人的思维不是这样的,中国人就想着“我如何能赚一百万”。这样想的话,第一条答案肯定是去银行抢,第二条答案可能是坑别人一百二十万扣除成本自己获利一百万,这种思维再想下去就肯定都是和“假冒伪劣”有关的事情,做出来的结果肯定是零和博弈——你所有的收益都基于其他人的损失。因此,如果你很诚实,你想要一个双赢(win-win)的盈利方案,你必须优先考虑如何帮别人赚一百万,而不是自己赚到一百万。

博客园要盈利,思维的重点就不应该在于自身如何盈利上面,而应该在于当前客户甚至是潜在客户如何盈利的问题上面。因此,我们无论讨论出书还是悬赏求技术求人才都好,第一个关注点是客户而不是博客园本身。例如悬赏求技术吧,想着别人一个题目悬赏¥1000那么博客园能收益多少是没有任何意义的,正确的思维应该是想想客户乐意为一个问题悬赏多少钱。例如一个问题,客户估计要自己解决的话要¥1000的成本,那么他的悬赏肯定在¥1000以下,例如他估计来竞标的人肯定不少,那么悬赏可能就是¥900,这时候我们就去思考博客园是否可能使用不超过¥900的成本找到一个合适的人把这个问题解决掉。

总结起来,就是以下步骤:

  1. 博客园能够为客户提供什么样的价值
  2. 博客园能否在特定的成本下完成该价值

产品化——前方200米处

是不是两步就能完事呢?实际上这和最终产品化还有一定距离。

我们来说说出书的事情,其实现在我们都知道有博客园出书团队,但到底找博客园出书的途径是如何的呢,对比其他出书途径有什么pros&cons呢,这些暂时都无法在方便的在博客园或者出书团队的首页上找到,使用搜索引擎也找不到,这就让出书看起来更像是一个内测(private beta)的服务。这时候我觉得博客园就需要一位PM(Product Manager)来把出书服务变为正式版了,至少是公测(public beta)吧,这样才能让更多的人接触到这项服务。虽然出书的人仍然只占一个非常小的比例,但完善的服务总能给需要出书的人一个更好的体验,同时对于读者来说也更有影响力。

因此,最终我们还要加上第三个步骤,从而变成:

  1. 能够为客户提供什么样的价值
  2. 能否在特定的成本下完成该价值
  3. 是否有人能负责产品化

最后,还是照例推介一下我自己的blog,喜欢的话可以进行订阅哦:

2007年5月21日星期一

Google Recruiting Event

今晚去听了宣讲会、交了简历、做了笔试。如果成功获得面试机会的话,明天下午就会有通知,紧张的等待中……

题目当然如传说中的那么基础,但是和其他学校考过的题你全无关系,甚至你不可能通过一份题考的技术范围来猜另外一份题考的技术范围。做完之后,也不知道自己到底算是怎样,大家都是大部分做对了然后错了一点点吧,但到底哪题是关键,错了就死定了,那是不知道的。所以现在除了等,也没办法预测什么,或者作出什么抉择。

比较奇怪的一件事情是,实习职位列表上竟然没有Interactive Design Intern一项,然而网站和海报上是有的,难道因为招的人少而且不属于需要笔试的职位所以在宣讲中以及笔试的职位列表上都去掉了?另外一件事情就是,我越来越觉得Associate Produt Manager职位有吸引力了,可惜Intern不要本科的,Full-Time才收本科毕业生。APM职位给我的良好印象是它什么领域都能接触到,Technology、Design、Marketing、Legal等等,而且Google招收毕业生为APM后两年就能转为PM,其他企业的PM一般都是SE(Software Engineer)凭借多年经验晋升而来的。如果真的有机会进入Google,就一定要试试自己是否有足够的实力获得APM的位置。

2007年5月17日星期四

Cat2 模板系列开始预览啦!

什么是Cat2

Cat2 = Cat * Cat,两位Cat合作的意思,也就是我Cat Chen猫窝猫影组成的小团队。

什么是Cat2模板?

这是一个XHTML+CSS+JavaScript的模板系列,暂时只包括blog模板,并且优先提供Blogger与WordPress立即可用的模板,同时也会考虑为其他常见的blog平台(例如DotText)提供立即可用的模板。

如何获取Cat2模板?

我们的模板存放在Google CodeProject Hosting,地址为:http://code.google.com/p/cat2/。您可以直接在Cat2的SVN上下载模板,或者预览模板的效果。暂时我们仅提供SilverBlack两个模板的预览,这两个模板都还没有100%完成XHTML+CSS的设计,大家可以根据预览的效果为这两个模板提出一些建议。

如何使用Cat2模板?

在我们的模板正式发布之后,您可以将我们的模板直接应用于您当前使用的blog,也可以修改后应用于您的blog或者其他形式的网站。Cat2模板使用的许可是MIT License

2007年5月16日星期三

What turns me on?

CC说我是一个非常清楚自己想要什么的人,这应该和我小时候经常努力争取到一样东西然后发现并非真正想要有关。现在我无论决定要获取什么,我都必须先确定我是真的想要,不是一时刺激让我对此有占有欲而已。

不过对于what turns me on这个问题呢,我却没有任何确认的答案,或许因为还没有足够多的like/dislike test呱,所以暂时处于untrained状态,无法预知我对一样东西最终的态度是like还是dislike,也就无法确认what turns me on。

2007年5月15日星期二

更新了 Blog Roll

大家如果看到“榜上有名”的话,可以考虑在你的blog上也增加我的链接哦。如果发现自己“下榜”了,则可能是因为你的blog已经长期没更新了,所以我就把链接给去掉了。

花了一整天终于把 Universal Resume 呕出来了

参考着几份不同人的简历,从去年8月份那份Google Camp简历上面把信息复制到新简历上来,同时又做了一些增删,最终做好了一份最新版本的通用型简历。另外还完成了简历翻译工作,现在有中英对照两个版本,要感谢的是Google Translate,很多时候不知道怎么翻译好的时候就参考它给出的翻译,能够获得很好的灵感的哦。

然后开始要做portfolio,因为申请designer类的职位都要portfolio啦,而且就算申请engineer类职位有个portfolio也没什么坏处。

2007年5月13日星期日

No Way Back

要学Piggest那样做到“谷底反弹”只有一个选择,就是告诉自己没有任何的退路。在大学那么久了,不是没有进步,而是总觉得自己可以吃老本,总是有后备选择,“最多不久怎样怎样”,这样下去就只能一直选后备方案。虽然后备选择也在plan中,所以看起来还是It was planned,但其实都是worst plan。

以前我们说起沟女的时候,说到“我们做你的坚实后盾”何解,意思并不是在你有危机的时候我们会如何出手帮你,而是说我们在后面顶着你把你向前推,让你毫无退路,如果你不奋力向前就会给我们压扁。现在我觉得我需要一个“坚实后盾”了,至少是一个reminder,在我想选择backup plan的时候提醒我不能再向后退了,必须全力奋斗才能survive。

Keep Busy

发现自己太过闲了,太多时间做一些wandering的事情。

在你的生活太专注于一个项目的时候,wandering一下还是好的,往往能够有不少意外收获,随机获取的信息总能激发一些创意,有助于当前项目的进展。但如果生活本身就是wandering,而且做的任何小项目都是wandering性质,那就死人了,出来的结果总是没有集中的价值,也就没什么实际用途。

从现在开始,我要设置一些目标然后keep busy,拒绝wandering,提高时间利用率。

2007年5月5日星期六

一个事件三种解释

最近读过或者在读的书包括:《魔鬼经济学/Freakonomics》、《蓝海战略/Blue Ocean Strategy》、《引爆流行/The Tipping Point》。这三本书都说到了纽约犯罪率持续上升30年后在90年代突然下降这回事,并且尝试用自己的观点来解释。

第一本书说,这是因为30年前的堕胎合法化,导致很多不应该出生的人没有出生,而这部分人如果出生了,因为他们来自未婚或低龄母亲,不可能接受良好的家庭教育,所以成为罪犯的可能性很高,现在这批人没有出现在世上,犯罪率就下降了。书中希望突出的是,“揭示隐藏在表象之下的真实世界”,传统人士认为正确的所谓“传统智慧”往往是错的,只不过更符合人们直观的逻辑思维,更容易被接受,所以例如加强警力导致此次犯罪率下降就不是真的,数据表明其他城市加强警力也不可能达到此次犯罪率下降那么明显的效果。

第二本书尝试强调执行蓝海战略时如何排除合作方的障碍,让大家都接受新战略规划。因此,书中认为加强警力是有效的,并且叙述了当时的纽约警察局长如何在这件事上实现了蓝海战略,排除障碍,让市民获得了更高的安全保障。

第三本书的副标题是“How Little Things Can Make a Big Difference”,所以它尝试把此次犯罪率下降解释为某一个little thing的结果,从而引爆的一次流行,让潜在的犯罪分子不再想着要犯罪。

其实这是非常好笑的一件事,同一个事件被用作素材而用于论证不同的问题,差别只在于论证的目标不同,所以引述的数据也不同。曾经在Google Sitemaps上面见到一句话,说使用相同的数据推导出不同的结论正是统计学家们的工作,看来这次真的一个明显的例子。

2007年4月28日星期六

No Junk Food Month

我现在决定把接下来的5月份定为我的No Junk Food Month,一个月内不吃任何垃圾进肚子里。

寒假的时候由于做手术,连续n个星期只能吃清淡的东西,看起来真的会让身体调理好的哦,至少脸上的皮肤会好一些。现在下决心一个月做成这样一件事情,以后才好下决心做其它的事情,咔咔……

明天是4月最后一天哦,是不是应该再趁机吃一次呢?

2007年4月27日星期五

放过一个,放倒一批

很久没写blog了,因为我的laptop坏了……现在还没有修好,只能暂时找台机来发发blog。

看了一下今年大四的毕业论文选题列表,不明白为什么有些摆明就是让无所事事的学生放过的,反正就是研究目标不明确,随便乱写也算是写了。而且听说别人完全通过Internet能够3天搞定一篇毕业论文,无需动手研究,这也实在厉害……

难道就没有人想过,每放过多一个不那么优秀的学生,对接收他们的组织来说整体质量就下降了一分,最终结果就是把整批人都放倒了?

以前在华附对这些事情是有明确规定的,例如保送生同一时间最多有两所高校同时在waiting,避免有些学校答应要你了而你又不去了。曾经有一年,华附有两个学生申请南大通过了,后来又不去了,结果当年高考南大马上做出报复性行为——有两个华附考生上了投档线但不录取,这在以前从来没发生过。类似的事情还有不少,所以华附吸取教训,确保保送生工作不会有损两所学校之间的关系。

为了保障更多人的利益,特别是后人的利益,限制个人的行为是理所当然的。不过中大这里好像很少强调集体或者集体荣誉之类的事情,所以每一个人都仅仅着眼于自己的利益,而没考虑到自己行为的外部性,从而陷入了一种恶性竞争中。学校本身也处于无意识状态,自然没有任何管制策略,甚至没有任何宣传策略,结果该管得多严就完全看老师的了,老师不想为难学生自然就松一些,至少很多人会被放过,结果是整批人被放倒,在外面得不到良好的声誉。

2007年4月9日星期一

根本不存在 DIV + CSS 布局这回事

在《欲练 CSS ,必先宫 IE》和《你有 <table /> 强迫症吗?》这两篇文章中,看到有不少评论用到div+CSS布局这个说法,用来和table布局比较。实际上div不是用来布局的,div只是用来表示一个其它元素都无法准确表达语意的一个块区,只有CSS是用于布局的,所以根本就不存在div+CSS布局这回事。反过来,table布局的时候经常依赖于CSS定义一个单元格的布局属性,所以可以说是table+CSS布局。也就是说,我们讨论的两种主流布局方法应该是纯CSS布局和table+CSS布局,如果你觉得你在用的是div+CSS布局,那么有可能你也有强迫症了。

接下来我们说说如何进行纯CSS布局,因为CSS布局依赖于XHTML,所以我们先要说说如何书写一个CSS无关的XHTML。其实书写CSS无关的XHTML并不难,虽然你不能再好像书写table布局代码那样集中精力于最重的视觉效果上,但其难度也不过是中学生写作文那样。

中学生写作文如何写呢?首先看看题目,然后想想整篇文章分为哪几个大的段落,每个大的段落说些什么,能够把你要说的东西说清楚。对于XHTML来说,这相当于用div把文档切割为几大块。这时候你不要想着这些div将构建一个怎样的DOM啊、CSS如何选择DOM中元素设置规则实现布局之类的事情,就大概划分一下文档的大区域就好了。

然后当然是用一些常用的手法来表现感情或者论证问题,这在XHTML中就是用特定的元素来完成一些常见的信息组织。下面就是信息组织形式与元素的对应列表。

img

作为内容的图片是一定要放到img里面的,这没有更好的选择了。然而如果图片不是作为内容,而是作为修饰性的,则千万不要用img。对于非内容的图片,应该在CSS中引用,而不在XHTML中出现。例如每一个导航链接有一个前导的箭头指示,那么这些箭头就应该通过CSS的background-image属性加上去,而不是直接作为img出现。

a

这也是一个非常准确定义的元素,链接都需要使用它。或许已经有很多人忘记了a的本意是锚点,其实这是一个十分有用的语义,你可以用它来标记文档中一些重要的引用位置。

ul, ol

ul和ol分别是什么意思呢?如果你回答不上来,却知道它们可以用来干什么,那证明你是被可视化工具宠坏了,要转换过来编写符合语义的XHTML需要先补充基础知识,这时候你最好先找一些看起来非常基础非常全面的XHTML书籍看看,因为没有扎实的基础你在上面构建更多的知识都是不牢固的。ul和ol其实分别代表unordered list和ordered list,也就是无序列表和有序列表。在语义上,它们都用于表示一类并列关系的内容,例如我们去商店购物之前列一张shopping list,上面要买的东西就是并列关系,在中文可以用顿号隔开那种。它们的差别在于是否有顺序,例如shopping list是没顺序的,先买什么后买什么是没关系的,但是一份旅游行程安排上面的景点列表却是有游览的先后顺序的。

ul常用于导航栏,因为导航元素符合上面所说的并列关系,树状导航结构还可以通过嵌套ul来表述。在这里,导航可以是我们常见的水平或竖直导航栏,甚至可以是地图导航,例如在中国地图上不同的省份热区其实是不同的li。如果我说,在主流浏览器上用户看到了中国地图和可以直接点击省份热区,在不支持CSS的浏览器上用户能看到一份纯文本的省份名称列表,使用的是同一份XHTML,而这完全通过CSS实现,甚至不依赖于JavaScript,你相信吗?

另外,如果你要显示一个图库的缩略图,这些图片也可以放在ul中哦,因为这些图片也是并列关系。它们可以自动先横排,排满一行就自动排第二行,CSS可以让他们乖乖排队,而不需好像table那样把图片定死在一个格子里。其实table用于布局就如同用监狱关押内容一样,把内容锁死在一个格子里不让它到处乱跑;符合语义的XHTML就如同一个开放的舞台,你只要懂得利用CSS的规则,内容就自然会找一个适合表现自己的地方站着。

dl

没有听说过dl吗?因为那些可视化工具生成的代码中从来不会出现dl?dl的意思是definition list,也就是定义列表。它包含的子元素不是li,而是dt和dd,也就是definition term和definition description。dl本身设计为字典单词与解释列表这样的语义,例如:
<dl>
<dt>Apple</dt>
<dd>苹果</dd>
<dt>Boy</dt>
<dd>男孩</dd>
</dl>

然而,如果你需要表示的的语义也是类似的,一个列表既包含定义也包含解释,那么也可以考虑用dl。

form, input

form也就是表单啦,这没什么好说的,就算再不顾及语义的人在书写XHTML时也会考虑到它与各种input对提交数据的影响,从而小心谨慎。

table

table自然是用来表示表格的,这不废话!如果是数据表,当然可以用table来表示,但如果不是,就最好别用table了。

人名列表呢?例如一个3行4列的人名列表。如果这12个人名是并列关系,我建议你用ul和12个li来表示,再通过CSS来让它们在一行内并列显示多个。名片表呢?也就是3行8列,每两列中左侧一列显示人名右侧一列显示电话地址等联系方式。我觉得dl在一定程度上能满足此需求,dt放人名,dd放联系方式,不过这时候就涉及了dl滥用的争论,因为人名与联系方式当作定义与解释有点牵强。

接下来还有一个关于你是否系统学习过XHTML的小提问,那就是你是否知道table下面的caption、col、colgroup、thead、tbody、tfoot元素及summary属性分别用于定义什么,还有就是你书写table时是否会使用thead、tbody。

div, span

再次审阅上面的列表,如果你需要表示一个块区却无法在上面找到更适合的元素,那么你就可以考虑使用div和span这两个最没有语义的元素了。div与span的区别,历史上的不说了,现在通常大块的区域用div,行内的小文本片段就用span。在上面我已经说了div一般用于全局划分为几个大的区域,所以一般不需要使用了。span其实也很少使用,因为行内的强调通常可以用语义更强的元素例如strong和em。

在理解上上述那么多常用元素后,写一个XHTML就真的如同中学生写作文一样容易啦,还是搭积木那样,其实和以前使用可使化工具搭积木没什么不同,唯一不同是现在你理解了你在搭的是什么,而以前你只在乎搭出你想要的视觉效果来。写代码与写作文所类似的地方,就在于你写得越多就越熟练,也就越能写出好东西来。在写好XHTML后我们就要开始考虑如何写CSS了,或许还需要在XHTML中略作修改以方便CSS中规则的选择与匹配,不过这是以后再说的内容了,今天就说到这里。

最后,如果你有兴趣阅读我以后发表的有关CSS的文章,可以考虑订阅我的blog:

2007年3月31日星期六

Imagine Cup 2007 Web Development 第一轮出线啦!

200多支队伍参加比赛,第一轮有30队能够出线,我们队出线啦,好开心哦!第一轮比的只是策划,把策划推销出去就行了,接下来要努力把实现做好,才可能在第二轮出线。如果真能第二轮出线进入决赛就好啦,能够去韩国哦。

2007年3月24日星期六

Adobe Apollo vs Joyeur Slingshot

如果觉得这世界上有Microsoft WPF/E vs Adobe Apollo还不够刺激的话,那么我们可以看看刚刚加入这竞技场的一位新选手:Joyeur Slingshot。Joyeur Slingshot是谁?我想你应该看看它背后那个阵营标记,没错,就是Rails这5个字母!

Slingshot有什么明显的好处吗?使用Microsoft WPF/E和Adobe Apollo都要将思维模式由B/S改为C/S,将设计重点转移到运行在客户端的代码上来,不再是考虑服务器端代码如何处理客户端请求,而是考虑客户端任务如何通过请求服务器端服务完成。然而Slingshot的思路不是这样,它希望Ruby on Rails的程序通过小规模的重写就能实现offline模式的支持,就如通过小规模的重写就能将整页提交改为AJAX操作。如果你不是很清楚这在RoR中有多少工作量,就想像一下ASP.NET用上UpdatePanel和Extender所需要作出的修改吧。

Slingshot是怎么实现的?其实大部分RoR程序员都是用“offline”的模式来开发的,也就是在自己机器上装一个RoR的平台。要在一般的客户端机器上装一个RoR并不难,也就和装一个.NET Framework差不多。Slingshot提供的就是客户端和服务器端同步数据的能力,至于何时何地同步什么数据,这就由开发者决定了。同时Slingsot还强调文件的拖放传输,你不在需要一个一个选择文件然后点击上传,在Slingshot附带的客户端浏览器中你可以直接将文件拖放到页面上实现文件选择与上传下载。

使用了Slingshot的框架之后,网站还能用不同浏览器访问吗?这显然是可以的,用户看到的还是老样子的RoR网站,只不过无法使用Slingshot提供的一些功能而已。Slingshot将于4月底发布,最终和WPF/E以及Apollo的对抗将会如何呢,让我们拭目以待吧,或许Rails将继续以它的敏捷性在Web2.0领域获得较大的市场份额。

2007年3月23日星期五

更新中……

之前一段时间只看技术书,有些麻木了,所以现在改为开始看小说,更新一下大脑里面的信息结构,否则我真的没创意了。

另外最近Blog*Spot又无法从大陆访问了,大家要访问我的Blogger最好通过Feed吧。

2007年3月19日星期一

Adobe Apollo Public Alpha

Adobe Apollo Public Alpha出来啦!一直尝试去学ActionScript,却一直都学不进去,现在机会又来了,直接学习AS3,到底我能否用Apollo做点什么东西出来呢?嗯……暂时还不知道呢。

2007年3月18日星期日

你有 < table / > 强迫症吗?

上次讲到“欲练 CSS ,必先宫 IE”,如果你宫了IE然而还是觉得不得要领,那就该怀疑自己是不是有传说中的table强迫症了。

CSDN社区上,时不时能够看到一些页面整体布局的问题,要求用div做一些table才能做到的,否则就以此为把柄说XHTML+CSS布局方法不好。其实,首先要做的是改变思维,以适应XHTML+CSS的布局。

面向页面设计而非面向浏览器设计

XHTML+CSS能够实现的是一种流布局,也就是随着内容的长度自动增长区域,并且最终导致整个页面增长,这时候浏览器就必须显示滚动条。table强迫症的一个征兆就是极力避免流布局,希望以浏览器的可视区域为布局目标,要求在可视区域中划分内容区域而不是在页面上划分内容区域。实际上XHTML是无法针对浏览器设计的,因为它仅仅包含语义,或者说是内容,而浏览器如何去表现这些内容是我们无法确定的。CSS提供了我们控制表现方式的一种途径,但这仅仅是针对主流浏览器的,而且浏览器支持的“指令集”还有稍为的差别(说到这,我真希望能够为一个浏览器写CSS然后编译为全平台兼容代码),最后这些指令暂时还仅仅支持针对页面的流式布局控制。因此,如果你决定要开始写符合语义的XHTML并且仅仅用CSS控制布局,首先就要把思路转变为面向页面(或者说是文档)的布局控制,而非面向浏览器可视区域的布局控制。

接下来肯定有人要说,“那你就是承认了有些布局老方法很容易做到,但新方法很难做到啦”。这是当然的,然而这不成为我们继续使用table的理由。这时候要反过来探讨原始目标了,我们是为什么要控制布局?低层次的需求是为了美观,谁都希望同样的内容能够以更好的视觉效果展示在用户眼前;高层次的需求是为了控制受众的浏览方式,让他们能够按我们预先设计好的方式来区分页面内容的轻重点,按我们的期望优先浏览某些内容,同时也帮助他们更快的找到他们想要的内容,而不会在我们的网站内感到沮丧。既然我们确定了这时控制布局的目标,那么我们再来看看CSS是不是“没办法把事情做好”。首先,CSS也能做出美观的页面,虽然某些布局做不到,但是在CSS的限制下做到同等美观程度的页面是肯定没问题的。其次,CSS也能让设计变得友善,不会说CSS的设计就肯定是“干净”到用户无法一眼找到他想要的功能。因此,虽然CSS无法实现某些特定的布局效果,但对于设计师来说它能够达到老方法所能达到的同等效果,这就足够了。

从XHTML中去掉内容无关的视觉元素

另一个table强迫症的征兆就是,习惯为每一个视觉上的元素对应一个XHTML元素。在table中,无论视觉效果有多复杂,我们总能不停的切割table,甚至table套table,直到准确定位每一个特定的元素。然而应用了CSS之后,这就是不必要的,甚至会给设计师带来麻烦,因为XHTML+CSS就是为了内容和布局分离,所以如果一个视觉元素与内容无关,那么它就不应当出现在XHTML中,自然也就不会对应一个XHTML元素。

例如有一个网站当前栏目的徽标,这个徽标没有任何的语义,而XHTML中也有文字内容描述当前栏目了,那么这个徽标就并不一定要对应一个<img />元素。如何让徽标显示出来呢?它可以是当前栏目文字描述区域的background-image,同时通过一些定位技巧让它显示出来。如果你认为有这个徽标就不需要文字描述时,你还可以通过定位技巧将文字隐藏掉,这样单纯看XHTML或者在不支持CSS的浏览器上就只见文字描述,而在支持CSS的浏览器中则看见会标。从这个例子,我们可以看得到一个视觉元素不一定要对应XHTML中一个实实在在的内容元素,或者对应一个文本元素而非图形元素。XHTML包含的是内容,那就不应该包含与内容无关的视觉元素描述,而通过CSS你可以事后增加有关的视觉元素。

又例如:before和:after这两个伪选择器,允许你创建插入在匹配元素前后的元素,这样就能够实现非内容视觉效果仅在CSS中插入。常见的用法包括,插入clear到浮动元素之后以确保父元素的完整包含,又或者是引用语句的前后自动加上引号。事实表明,CSS是很适合于将非内容的元素从XHTML中分离出来的,因此我们在设计XHTML时就不能够总想着要有什么效果,而应该单纯想着信息的组织形式。

最后,如果要我为table强迫症开处方的话,我还是会选择《CSS Mastery/精通CSS》。看完之后,你自然能够解除上述的烦恼,理解到CSS布局带来的便利,从而选择开始用纯CSS的思维来进行设计。如果你关注CSS方面的内容,可以考虑订阅我的blog:

将来我会和大家分享更多关于CSS的思考,以及使用CSS的技巧。

2007年3月16日星期五

国内 Web 2.0 网站缺少的一个按钮

无论是web2.0网站,还是一些比较奇特的blog,都缺少一个about按钮。

明白我在说什么吗?你的网站太创新了,我都不知道是做什么服务的,又不在显眼的位置放个about,那我就真的不知道自己是否应该注册了,或者说是值得注册。有些网站还好,在页面底部的导航栏有“帮助”或者“规则”这样的东西,但这显然也不如直接在页面顶部导航栏放一个about好,因为谁会注意到底部导航栏呢?

最近我们学校有一个学院书记就大胆说出了中国学校门户的不好,“都是那些给当官的人看的新闻”,他认为学校门户就应该学习国外的,应该是服务导向的,也就是说学生和教师应该能够很方便的通过网站获取到自己需要的服务。举个例子,如果你是新生,登录你们学校门户,你知道该从哪里查询成绩吗?相信大部分人都不知道,必须有人告诉过你,你才知道哪个链接哪个链接走下来就是查询成绩的地方了。

这就是中国网站的缺陷,中国人就是特爱面子,就是纯粹做show,完全不理会网站必须服务了目标人群才算是成功的网站。

2007年3月13日星期二

什么样的 Code 更像是 Configuration

Code is Configuration这篇文章中,说到了我对Ruby on Rails优点的理解,那就是RoR的代码相当于是配置,所以能做到习惯优于配置。有人说,这是动态语言的优点,然后把动态语言和静态语言区分开来讨论各自的优劣,然而我觉得这是不能绝对划分的,语言的动态与静态是一个过渡。

举个例子,virtual函数的override也算是一种动态,因为程序是运行时查表寻找最顶层的override函数然后执行的,和所有的动态特性一样,这需要牺牲一些效率。然而我们没有把C#称之为动态语言,因为它还是要经过静态编译才能执行,但这不妨碍它拥有部分的动态特性。因此,语言不能简单的话分为动态与静态两类,这之间是可以平滑过渡的。

既然语言没有动态与静态的绝对划分,那么代码就是配置的概念也应该没有绝对的划分,我们只能讨论怎么样的代码更像是配置。为了说明为什么代码可以说是“更像配置的”,这里就再举一个例子。同样是创建一个复杂的Toolbar,在MFC里面大部分的代码都是用来“实现”这个Toolbar的,虽然MFC已经对Win32API进行了封装,但使用起来还是很像调用API的风格。而在WinForm创建复杂Toolbar的话,大部分代码都是创建控件然后设置属性,这样看起来就更像是“配置”。所以使用WinForm的代码比调用MFC的代码更像是配置。注意我这里说的是“更”,既然存在B比A更好,那就还可能存在C比B更好。比WinForm更好的是什么呢?WPF和C# 3.0,前者能够隐藏更多的实现细节,后者能够让属性设置代码更简洁,让代码看起来更像是在配置控件。

如果抛开执行效率不说,给你设计一门全新的语言,应该如何做到Code is Configuration呢?这先要看看单纯的Configuration是怎样的,例如XML,抛开背后的实现细节,单纯用XML来写程序,或者说是“配置程序”,你又愿意吗?肯定不愿意,因为虽然它的层次结构好,然而写起来很麻烦,和自然语言的差距太大了。因此,理想中的语言应该尽可能贴近自然语言的情况下,同时也能够尽量隐藏实现细节提供高度抽象的可配置性,而后者通常越是动态语言就越容易做好。说到这两点特性,你是否突然想起一门正走向消亡的语言呢?贴近自然语言一直是BASIC所强调的,而同时又是动态语言的,那就是VBScript了。然而最大量使用VBScript的应该就是ASP了,而ASP正在逐步被ASP.NET取代。或许将来有一天,VB.NET能够重新加入动态语言的特性,配合非编译的ASP.NET,能够打造一个好像ASP那样适合RAD的ASP.NET。

2007年3月2日星期五

Vista + GeForce 6600GT = 不给我玩游戏

游戏过程中,甚至是Vista自身的3D屏保,都可能出现问题,接着黑屏。幸运的话,过一会儿能够恢复显示,并且提示“显示器驱动程序 nvlddmkm 已停止响应,并且已恢复”;不幸的话,整台机死掉,只能reboot。

用Google搜索"nvlddmkm",发现问题出在NVIDIA的显卡驱动上,而且出问题最多的就是6600,哎……怎么这么不幸。而有些人通过重装驱动,或者停止超频甚至是降频解决了问题,说明这可能和主板、内存的稳定性有关,可惜我暂时找不到有效的解决办法,只能怪主板太cheap。

2007年2月25日星期日

还有新鲜感吗?

ZDNet有个家伙,用了19个月的Vista,说至今仍然感觉到Vista有新鲜感,大家可以去看看他的文章:Windows Vista - 19 months of usage and counting

在跟别人聊起这新鲜感的事情的时候,我说这19个月的测试版是当然可以让人保持新鲜感的,因为不断的有upgrade和fix——原来有问题的功能变得没问题了,原来没问题的功能变得更好了,这当然让人能保持新鲜感。然而如果连续用19个月的正式版呢?正式版发布后,或者准确来说你给钱购买后,就不会再增加什么新功能,因为你以前给钱了,出新功能不可能赚到更多的钱,这时候新鲜感就会自然下滑。当然,XP SP2是个例外情况,因为XP的安全问题严重,必须增加安全中心这个新功能去巩固用户信心。

说完这段话,突然感觉到这是个类似GF和Wife的隐喻,在你最终结帐之前可以不断更新功能,这样你也就有持续的新鲜感,但结账之后就……

不过现在Web2.0啊,大家都标称自己是永远beta,永远在改进,其实和mm的关系应该也能这样啊。有很多人的思想被限制在当今的一夫一妻婚姻制度里面,对以前的一夫多妻不敢恭维,对将来的制度也不敢想象,就如此固步自封,其实是不对的。我们应该尝试去探索一种新的人与人之间的关系,例如好像我这样,寻求一个永远beta的mm,咔咔!

现在的软件已经开始进化,Software as a Service(SaaS)慢慢会流行起来,甚至连Vista也被设计为如此——将来Vista的升级就会如同一种服务一般设计,你了一付费使用这种服务了,就能得到升级,这种升级是能够确实让系统功能能够得到明显更新的。购买费用足够高的Ultimate版本,其实也算是包括了购买服务的费用,因为Ultimate版本在Windows Update时能够下载所谓的Ultimate Extras更新,属于能够明显增加系统功能的更新。看来突出长期的服务长期的交易将成为潮流……

Anyway,其实没必要将那么多SaaS之类的东西,我在说的是新鲜感。我不会随便换操作系统,安装任何一个操作系统都经我细致调教以保证在我通常的使用情况下能够有best performance,但我希望它好像Vista那样能够有不停的增值服务,甚至更多的更新以保持新鲜感。嗯……没错,我说的就是这个东西,我需要一个类似这样的mm,我不需要结婚。如果可以,我需要一种新的license来维护这种关系双方的利益,但不要婚姻法。

2007年2月23日星期五

Code is Configuration

Ruby on Rails强调Convention over Configuration,也就是习惯优于配置,这对我来说是一个很有吸引力的特性。.NET是配置优先的(据说Java也是),最好什么都不是硬编码而是可配置的,开发出来的产品最终可以在部署时根据实际情况配置,或者再被调用时按照调用者的需求配置。为什么RoR可以是习惯优于配置呢?如果什么都硬编码了,遇到需要改动的情况怎么办?按照我现在对RoR的理解,思考了一下,寻找到一种可能的解释——RoR的代码也就是配置!

在.NET里面,函数或者是指令,都是描述行为的。一组指令组成一个函数,代表着一组顺序执行的行为;之后一组函数组成一个类,代表着一类行为的聚合。而RoR指令给人的感觉却不是这样,RoR中有些指令完全可以认为是描述性的,就好像配置一样,而且是顺序无关的。

举个例子,创建数据表的migration,你可以认为它是ruby语法写的CREATE TABLE,而且它做的也就是CREATE TABLE,但实际上它是schema!它已经描述了数据表是怎样的了,而不仅仅是生成数据表,生成数据表仅仅是migration执行时瞬时的行为,生成数据表之后Rails自动提供ORM功能,不再需要另外的schema或者自动生成的code来重复描述schema。这正是其符合DRY原则的地方,在整个RoR应用当中,只有migration这一处描述了schema,其它地方均无再次描述schema的地方。然后再对比一下我们在ASP.NET中的做法,重复描述数据表schema的地方实在不少,无论使用哪种ORM工具都如此,希望以后大家都能用上DLinq从而避免这个问题。

再举个例子,model中可以直接用helper函数指定每一个属性要符合的业务逻辑,从而提供验证功能。还记得我们在ASP.NET中这有多麻烦吗?首先,根据决不信任客户端提交原则,在View中要验证数据,验证一方面要能抵御恶意编写的提交,另一方面要对不符合业务逻辑的提交给出反馈,这辛苦了Validator控件,而且我们自己也要写不少代码(如果部分输入设计复杂验证逻辑的话)。接着,业务逻辑层再来一次验证,这是为了业务逻辑层的完备性,在脱离原有表现层而被第三方调用时也能确保数据的合法性。然而在RoR中却不用那么麻烦,在model中一次声明规则就行了,view中输入的数据如果不符合model的规则自然会在view中得到反馈,这种逻辑既是声明性的,又符合DRY原则。

最后,说说ASP.NET可以从RoR学习的规则。首先,MVC分离是有必要的,仅仅垂直分3层是不够的。我之前也有想过,为什么要在表现层提供众多的Validator控件,而不是在业务逻辑层提供验证相关的Helper类呢?这些Helper类,加上业务逻辑层反馈到表现层的通道,就能够做到只需要实现一次验证。或许Validator控件,也是简化表现层设计的一个做法吧,为了吸引别人学习ASP.NET,MS做了很多东西让它看起来很容易用,却让人难以全局协调。其次,一个类象征现实中一类事物的隐语仅仅是针对model的一个说法,其它时候我们更需要函数,类就仅仅是用来聚合函数。例如一条验证规则,这是一条配置,同时也是一个函数调用,需要完成验证这一顺序操作的是函数,而不是类。如果你要用类去表示规则,例如config文件映射出各个ConfigurationSection那样,这就增加了处理步骤,因为最终你还不是把类里面的属性读取出来交给函数处理这条规则。

2007年2月18日星期日

难得写影评

从来没写过影评,难得写了点相关的东西,原本发在TLF上面,得了原创精华,现在贴过来。

The Prestige 中的“执着(obsession)”

好像有不少人提问为什么Tesla不拿复制机去复制钱,为什么Angier要淹死自己,之类的问题。还记得Tesla关于obsession所说的吗(TLF版字幕翻译为“执着”)?这部电影里的人可以认为都为obsession所困,只有Cutter是清醒和实在的。

obsession可以翻译为“执着”,但显然有一定的贬义,因为这种执着首先强调的是你无法拒绝去想一样东西,你总是想着总是想着,没办法再专心干其他事情。其次,你想着的这个事情通常都是所谓的妄想,根本无法做到的,甚至你自己都清楚这一点,这显然导致所谓的“认知失调”,也就是你的两种认知互相矛盾。

你自己经历过obsession吗?如果你经历过,那么你很容易理解这些人的行为方式。如果你是一个很实在的人,不曾有过obsession,没有过例如患得患失之类的感觉,那就不那么容易理解了。

Tesla到死的时候都是被定为疯癫的物理学家,后来才追加那么多荣誉给他,可见他生时的obsession是多么的强烈——他在做很多常人认为常识之下根本不可能做得到的事情。Tesla发明了交流电相关的很多专利,还是无线电之父。想一下Tesla的那个true magic,无需导线都能点亮的灯泡,现实中的Tesla确实有研究过无线供电,不过电影里假设他已经研究成功。无线电我们已经用上了一个世纪了,然而无线供电却依然没有实现,可以想象无线供电在当时是多么天方夜谭的事情。无线电本来就可以用来赚去足够多的钱,交流电也是。Tesla完全不去赚钱,去研究无线供电,而电影中的版本则是在实现无线供电之后就帮Angier做那机器,事实上是可以理解的,这就是所谓的obsession。

Angier的也是obsession,他相信他所说的“一切魔术皆可复制”,另一方面Borden的魔术他复制不了,这是显然的认知失调。所以无论付出什么代价,他都想要超越Borden,这不是为了超越本身的个人感受,而是不再让自己所相信的“一切魔是皆可复制”受到自己所理解的现实的质疑。这种自己对自己的质疑其实是痛苦的。解释认知失调的常用例子是喜欢吸烟的人也知道吸烟有害,这时候一个人的两种认知——“我喜欢吸烟”和“吸烟对我身体有害”都无法辩倒对方,这两条都是正确的,这时候就会让人很不爽。而在Angier这个问题上,就不是不爽的程度,而是痛苦。

Angier还有另外一个obsession,就是她妻子死的时候的感觉。还记得他在洗手池憋气的情景吗?他没办法理解淹死到底是好的感觉还是坏的感觉,大多数人的常识,包括他自己憋气的结果,应该都是觉得被淹死是痛苦的事情,然而这时候非常不幸被Cutter说了一句“就像回家的感觉”,这就出事了……这同样是一个认知失调,两种理论都无法证明对方的错误,然而有第三方的办法可以证明,那就是把自己淹死。Angier选择了稍微更相信Cutter的说法,所以Cutter告诉他真相的时候也就再次出事了……

最后,Borden也不幸染上了obsession,这可能与他说过“我就有一个魔术你无法复制”有关,所以才一定要跑去后台看,这最终导致了他的死刑。当然也可能由于他确信自己的牺牲已经足够大,并且他看不出Angier有什么所谓的牺牲,所以他认定Angier做出一个更好的魔术来是不可能的事情。

个人认为,就算这部电影叫做"Obsession of The Prestige"也不过分,因为讲的就是obsession有关的故事。一个魔术,Cutter之所以说常人不能看穿它,是因为不愿意去往能看穿它的方向想,就在于常人都不希望自己是认知失调的,希望自己当前的认知是非常可信的,例如同情心驱动你尽量不去想笼里的鸟被夹死了这个可能。

为什么写“执着(obsession)”

写这篇评论,是因为在TLF看到很多很多人都会提问为什么某人不按照常规的利益最大化的思维来考虑,这是不是意味着只有少数人经历过明显的obsession?

有些事情曾经让我挺obsession的,所以我想知道这是怎么回事,是不是有另外一些人则能永远保持简单思维,不仅仅不会obsession,也不可能理解别人的obsession。

2007年2月16日星期五

学习 Ruby on Rails 真的很爽!

最近开始看《Agile Web Development with Rails 2nd Edition》,发觉感觉真的非常爽。虽然至今连Ruby语法都没完全懂,懂了的也没记住多少,但在看书过程中你会乐意模仿书中所有的例子,一个一个完成看看结果是怎样的,观察这个神奇的框架如何将你所书写的一点点代码转变为使用ASP.NET要不少代码才能实现的功能。

我不知道它是怎么实现的,但暂时我还不觉得我有必要知道它是怎么实现的,因为那种流畅的感觉太好了,所谓的"Convention over Configuration",让我完全可以凭感觉去写,而不是好像用ASP.NET的时候那样想着“你这buggy又trappy的家伙,幸好我知道你底层是怎样实现的,我可不会随便踩进你的陷阱”。既然我不用了解它,就能流畅用好,为什么我还急着去了解那么多呢?

至于"Don't Repeat Yourself"的好处,暂时没体会到。ASP.NET中要坚持DRY原则的话,要在设计上下点功夫,而我现在用RoR才写那么几行代码,根本谈不上设计。

已经很久没有一本书让我阅读过程之中有如此的兴奋感了。之前看《Programming Microsoft ASP.NET 2.0 Applications: Advanced Topics/ASP.NET 2.0高级编程》,虽然也很有feel,但那种feel不是学到新东西的兴奋,而是自己辛苦用reflector看代码查资料领会到的知识得到印证的感觉,觉得有这样一本系统讲述这些底层知识的书在手边,快速看一遍,以后就知道如何查找了,比用reflector慢慢最终慢慢理解要好多了。这种感觉准确来说,只是所谓的“松一口气”,ASP.NET给入门者的印象是很简单,然后你发现很复杂,接着你松一口气因为有人帮你将复杂的知识归纳好印成一本书方便你查阅。

看现在这本700多页的RoR的书,我能够一个晚上看50多页,因为一旦开始看了就真的不想停手。曾经有人评价RoR能让程序员惊叹“这不就是我梦寐以求的Web开发方式”,而阅读这本书的过程就让我信服了这个评价,并且推动着我一直想看下去,因为我总会想着“还有什么我梦寐以求的事情还没被我发现呢”。

2007年2月14日星期三

欲练 CSS ,必先宫 IE

“Win国天下,欲练CSS之人不在少数,大多不得要领,又或是走火入魔,全为IE所累。故曰:欲练CSS,必先功IE。”

曾经,我也属于为IE所累的行列,如今见到很多人仍然不愿意对自己的宝贝IE下手,所以决定特异写篇文章说说此事,以明辨IE到底是宝贝还是累赘。

好了,funny部分结束,按回我的习惯直入正题。之所以说IE不好,是因为IE会误导了你对CSS模型的理解,让你以为IE的理解是对的,之后无论如何你都无法用你的IE模型理论去为你那个无法在FF正常显示的CSS提供fix。更加坏的事情是,即使你仅仅针对IE设计,不考虑其它浏览器,由于IE模型绝对可以说是一只让人难以捉摸其脾气的怪物,所以你单纯为IE设计也会遇到众多难题,发现很多的效果总是绕来绕去都难以实现。

我们都知道,XHTML+CSS的目标就是实现内容与表现分离,理论上对于任何特定一份内容,我们都可以通过CSS实现任何我们想要的表现形式,或者细致地说是布局形式。虽然现实与这个目标有一定差距,但是CSS已经能够满足大多数常见的布局需求,这有CSS Zen Garden为证。然而如果你用的是IE,因为它难以捉摸,所以如果你想用一种简单优雅的CSS去让IE能够实现“任何你想要的布局形式”,那是不可能的,只有复杂繁缛的CSS才能够在IE上满足你的需求。我曾经提到过一种理论,“一个人对一个研究方向是否感兴趣很可能是完全靠偶然事件决定的,这就好像人第一次打羽毛球,如果你赢了几盘你就会感兴趣,如果你一直都赢不了你就会没兴趣”。IE在需要复杂繁缛的CSS这一点上,就足以令大多数的入门者却步。你总感觉到不得要领,你自然没兴趣学下去。

举一个例子说明这个问题,例如你不知道IE有hasLayout这回事,一个元素是否hasLayout对它的布局方式有重大影响,于是你肯定用最简单的思维去思考CSS,认为不同的CSS规则之间应该是松耦合的。“CSS应该被设计为简单优雅的”,你肯定会这样想,没错,它确实被设计为这样,不过IE不是这样去实现CSS罢了。我们用下面的代码去证明IE在quirks mode与standards mode之间的区别:
<div style="background-color: red; height: 30px">
  <div>Hello</div>
  <img style="float: left; width: 200px; height: 160px" src="blank.gif" />
  <div>Hello</div>
</div>

首先,我们用quirks mode看看结果如何,并且一个初学者看到这样的结果会去如何理解CSS规则。在quirks mode中,我们可以看到背景为红色的<div />包含了上面1行的文本,以及下面向左浮动的<img />(自然也就包括在浮动块右边的文本),在这里,我们可以建立两种认识:

  1. 容器是完整包含内容的,当内容的总高度比容器大的时候,容器就会自然伸展以确保容纳内容。
  2. 浮动块也属于上述条件所要求通过伸展以确保容纳内容。

以上规则是完全错误的,一个懂得标准CSS以及理解quirks mode的设计师将会如此解释他的理解:

  1. 因为IE在quirks mode中会将height理解为min-height,所以它认为<div />的高度不小于height指定的30px即可。而根据CSS标准,当height设置为30px时,高度就一定是30px,超出部分如何处理则由专门的CSS规则决定。
  2. 因为<div />被设置了height属性,在IE中这就让它hasLayout了,这就导致它一定要包含所有的内容,包括浮动块。而根据CSS标准,浮动块是无需被完全包含的,它就浮动在那里,除非遇到设置了clear属性的元素,否则后继内容只会侧移避让。

好了,相信这个对比足以说明问题的严重性了,通过IE的效果去理解CSS,最终只会让你的理解与真实的CSS相差甚远。详细的standards mode与quirks mode带来的标准执行差别,可以参考这篇文章:CSS Quirks mode and strict mode

然后肯定有人要问我,如果通过doctype确保使用的是standards mode,那是不是就没问题了呢?standards mode确实会让IE对CSS的解释合理很多,但事情并没有那么简单,这你可以通过实践去慢慢体会。你可以尝试在standards mode中设计CSS,并且尽力保持它们在IE/FF/Opera/Safari这4大主流浏览器中显示一致,随着设计的进行,你会发现这不是那么容易做到的。或许你不乐意花时间去fix其中的一些小问题,宁愿任由其中一些浏览器的用户看到比较丑陋的布局,但至少你已经了解到一个和上面例子类似的道理:不同浏览器即使同样在standards mode,其对CSS的理解仍然有所差异,而差异当中最多只可能有一个是正确的,甚至可能全部都是错误的。这篇CSS contents and browser compatibility就列举了众多浏览器对CSS支持的差异,一份CSS总会因为其中有一些规则在某些浏览器上是不支持的或者是buggy的,而导致你难以保持它们在不同浏览器上显示一致。

接下来可能还有人会问我,既然IE的市场份额最大(特别是在入门级的用户当中),又或者说我的客户指定使用IE作为客户端,仅仅针对IE设计CSS不好吗?为什么要针对FF之类的标准浏览器设计CSS然后再为IE进行fix?因为IE难以捉摸的脾气,让你无法将它的行为理解为一种简单优雅的规则,然后让你陷入CSS规则高度耦合的困境中。请看下面的例子:
<div style="background-color: red; border: 2px black solid">
  <img style="float: left; width: 200px; height: 160px" src="blank.gif" />
  <div>Hello</div>
</div>
<div>Hello</div>

现在,你在IE中看到的效果应该是左边出现<img />,然后两个<div />内的Hello都向右偏移以避让<img />这个浮动块了,其中上面的<div />仅仅占用移行的高度,因为它没有声明高度,所以就是自然高度,也就是一样,这些都很好理解,所有规则都是解耦的。然后向例子中增加对第一个<div />的width属性复制,看看结果会如何:
<div style="background-color: red; border: 2px black solid; width: 600px">
  <img style="float: left; width: 200px; height: 160px" src="blank.gif" />
  <div>Hello</div>
</div>
<div>Hello</div>

这时候第一个<div />完全容纳了<img />,把第二个<div />挤到下面了。这该怎么解释呢?我们可没有设置它的height属性哦,难道又犯之前例子所说的因为hasLayout而必须容纳所有内容?正解,这就是IE难以驯服的地方,一个应该是完全独立的width属性,设置之后引起了高度以外的其它影响,这让人无法尝试以一种简单优雅的方式去理解IE的行为。这就证明了,如果你要学习如何为IE设计CSS,就先要学习标准CSS,再加上对IE怪异行为的理解,比仅仅学习如何为一个标准浏览器设计要难多了。这时候你是不是想说,“如果客户愿意放弃IE,甚至全世界都愿意放弃IE,那就实在太美好了”,没错,这才是正确的想法,一心想着仅针对IE设计以求方便只会让你走火入魔。

最后,如果你已经有了一定的CSS基础,对CSS规则都理解无偏差,却缺乏组合CSS规则的想象力,无法做到所谓的“实现任何你想要的布局效果”,这也就是说,你的内功已练成,仅仅差一些表面的套路,这时候我推荐你去看《CSS Mastery/精通CSS》。看完这本书,相信你只会觉得自己缺乏布局的创造能力,而不会有布局却不知道如何实现。另外,如果你关注CSS方面的内容,可以考虑订阅我的blog:

过年之后,我可能会写一些与ASP.NET+CSS有关的文章,因为现在ASP.NET+CSS的开发并不方便,即使用了ASP.NET 2.0 CSS Friendly Control Adapters也如此,因此需要根据自己的实际情况定制配对的Control Adapter才能解决问题,这就是我接下来要研究的事情。

2007年2月12日星期一

IIS7 会重用那些不该重用 HttpHandler

首先,实现IHttpHandler时要实现一个IsReusable的属性,这个属性告诉ASP.NET此HttpHandler是否可重用。如果一个HttpHandler是可重用的,那么多次请求都有可能用同一个HttpHandler实例;而如果一个HttpHandler是不可重用的,那么ASP.NET应该确保每次请求使用的都是一个新构造的HttpHandler实例。

Page是设计为不可重用,所以每次请求都会导致构造一个新的Page实例,这是因为Page的生命周期不能恢复到初始状态,一个Page经历完生命周期后就不能用于处理下一次的请求。类似的,如果我们有一个HttpHandler有类似的性质,处理一次请求后其状态就难以恢复到适合于处理下一次请求,或者说恢复还不如构造一个新的,那么我们就应该设计为不可重用。

我在做一个通过IFrame提交的无刷新上传控件,这东西包括一些HttpHandler,为的是能够直接关联到axd后缀而用于路径无关的场合。其中有一个HttpHandler我直接继承自Page,并且写得好像aspx+cs编译出来的代码那样,在OnInit阶段构建完整的控件树。这个HttpHandler以前在XP的IIS5上一直没问题的,但到了Vista的IIS7就出问题了。

先说明,在IIS7我采用其新的配置模式,将<httpHandlers />配置在<system.webServer />节,而不是<system.web />节,这是模仿着ASP.NET AJAX的web.config做的。做好之后就发现问题了,这个用作HttpHandler的Page竟然时候抛出Exception,说Page上面有多于一个的HtmlForm。仔细检查后确认,我的Page代码确实仅仅添加了一个HtmlForm,并且这个Exception不会出现在编译后的第一次请求,于是我就怀疑Page被重用了,所以OnInit被多次执行,这才可能导致它有多于一个HtmlForm。我启用了Page的Trace,在Render中注释掉base.Render,并且用Trace输出Page上的HtmlForm数量,发现真的是每次请求都会导致多一个HtmlForm,这基本上可以肯定是因为Page被重用了。

然后我就用Google搜索,结果发现forums.asp.net上有人提出了完全一样的问题,他也是用Page来做HttpHandler。我想只有拿Page来做HttpHandler的人才会遇到这样的问题,因为一般自己写的HttpHandler都是无状态的,所以都是可以重用的。而那张帖子只有管理员回复了一句,“你最好去forums.iis.net问吧”。于是我就去iis.net搜索,结果发现没有人提到过这个问题,于是只好自己去论坛提问,可惜等了一天都没有人回答,看来IIS7普及之前iis.net的人气都不会上升(IIS7的默认欢迎页面链接到iis.net)。

最后,我选择了先采用Jeffrey Zhao建议的work around,就是再制作一个HttpHandlerFactory,它负责每次返回Page的新实例,然后在配置中改用该HttpHandlerFactory。这个解决方案实验证明是可行的,就是多一个类而已,不知道性能损失有多少。如果有人知道这个问题的官方解决方案的话,或者有一个更好的work around,请告诉我,谢谢。

2007年2月5日星期一

Microsoft AJAX Library + ADODB = ?

最近做了一个基于Web的纯桌面端数据库应用,非常轻量级的,在挑选库的时候最后还是选择了自己熟悉的Microsoft AJAX Library,而没有使用prototype、dojo、YUI之类的。一方面,是因为Microsoft AJAX Library比较贴近我熟悉的控件模型,另一方面要做的东西真的轻量级得只需要普通的控件,不需要拖放和效果,不需要封装新控件或widget。

过程

整个制作流程大概是这样的:

首先,我设计了一套CSS模板,使用Sharepoint Designer来做,配色方案是临摹着其他漂亮的table排版模板做的。在这个过程中,我真的觉得Sharepoint Designer非常好用,你需要做的仅仅是用XHTML将模板所需要用到的语义全部写好,然后开始在<style />内部加CSS,只要CSS技术过关,很快就能把整个页面格式化为你想要的效果,而Sharepoint Designer的IntelliSense则能够自动提醒你哪个class下面有哪个id或者有哪些element。在格式化好页面后,把CSS从<style />移动到独立的CSS文件,并添加对一些特定的浏览器的修正,那就搞定了。

接着,开始根据程序的需要,在XHTML上设计Page、Form等的一些常用结构,并且写了一些基本的函数来实现Page直接的导航,Form内的常见操作。我用的仅仅是函数,这些函数根据性质以类来聚集,但没有做一些代表实体的类(例如Page类),因为我相信这个东西足够简单,通过函数完成简单操作就行了,没必要将XHTML元素映射为类(控件)。在搞定了如此简单Page划分之后,Page之间就完全解耦了,然后我就可以独立设计每一个Page里面的逻辑,而Page之间的数据传递则通过切换Page的函数负责,这有点类似QueryString的做法。不过我这里Page的概念仅仅是一个<div class="page" />的元素,而传递的可以任意JavaScript对象。

数据库的操作,我反而没有做任何的封装,直接new ActiveX("ADODB.Connection"),然后就拿来用。很raw的形式,写起来的代码有点点像ASP的feel,不过ASP是直接输出XHTML,而我是构建DOM元素。实际上所有的CRUD操作都是以最直接的形式执行的,几乎不存在任何业务规则,所以也是直接把CRUD的操作封到函数里就行了。有趣的地方在于,Sharepoint Designer在一个对象赋值为ADODB.Connection后,竟然能在IntelliSense提示Open等方法,不知道这是对于ADODB等常用对象的支持,还是对于任何COM对象都提供支持。

最后需要做的,就是在XHTML上把整个界面填充完整,把函数钩上事件,这就完成了。没有任何数据控件,不曾用到ITemplate,用得最多的无非是TextBox和Button。

总结

即使完全不用于AJAX开发,甚至是没有服务器端的开发,Microsoft AJAX Library也是一个很好用的库。如果你习惯MS那套控件设计的思想与风格,那么Microsoft AJAX Library会显得很容易适应,不过前提是你已经有很好的JavaScript基础。

现在国外有一个争论,就是一个JavaScript的入门者是否可以使用库,还是说有能力自己写一个库的人才应该使用库。这个争论的来源,我觉得是因为现在的库还是太过像手脚架了,能够帮熟练的使用者提高开发效率,提高代码质量和软件健壮程度,然而对于入门者来说却会因为对基础问题的不理解或者理解偏差而到职他们在使用库的途中犯下更多的错误。

在这个问题上,我使用MFC时就有切身体会,我就觉得熟练使用Win32API的人使用MFC才能获得最大的便利,而入门者使用MFC则对着MSDN左翻右翻也不得要领。我不懂Win32API,曾经学过一点点MFC,站在VB Form或Win Form的角度来理解,总是觉得MFC设计很怪异,为什么如此多地方设计得如此命令式而不封装起来呢?在使用MFC过程中碰到自己无法解决的问题时就更麻烦了,然而懂Win32API和熟悉MFC的人却能很快告诉我——例如这是因为MFC自身的一个设计缺陷,或者是它迁就Win32API使用方法或风格的地方。

MFC就是一个如此的手脚架,让懂Win32API的人能够更高效地做一些事情,而不懂的人却无法得到抽象带来的便利,因为它并没有怎样抽象,只是将函数式API封装为对象。现在的多数JavaScript库也就是做到了这个程度,将最初的DHTML到如今标准化的DOM API进行封装,却没有太多的抽象,所以用起来还是要清楚它到底干了什么。这是因为遇到了问题有可能无法在高层解决,而必须回到低层解决。

回到争论上面来,我个人支持库的使用者必须有能力完全理解库这一立场。考虑一个不太了解JavaScript的ASP.NET开发者,他使用Microsoft AJAX Library,无论是使用XmlScript还是JavaScript,他的“活动范围”也就是sample的附近,一旦超出了这个范围,他就在“凭运气写代码”了,这时候代码质量可想而知。如果他开发出来的东西还有给其他开发者调用,那就更危险了。这种现象其实也普遍存在于ASP.NET的控件开发者中,对ASP.NET理解有限的开发人员制作出来的控件就有可能和给使用者带来麻烦。

至于什么时候能够有高度抽象的Web开发框架……想一想,MFC诞生于1992,而Win Form以来的.NET Framework诞生于2002,这估计要10年吧。或者乐观一点,VB Form的抽象也很好了,因此Win Form才那么多参考其设计,拿比较成熟的VB6来说吧,诞生于1998,也要6年。这样说来,可能也要到2012才会有高度抽象的Web开发框架出现。况且,抽象也依赖于硬件发展,当我们不再为“小小一段”ViewState占用多少流量与带宽而烦恼时,抽象才有可能实现。

2007年1月29日星期一

“都几唔痛下㗎喔”

做完手术啦!做嗰时有麻药,就真係唔痛嘅,之后慢慢开始痛,越嚟越痛……都係上床瞓觉好过啦,反正瞓着觉就唔知痛。

2007年1月27日星期六

非常好用的 Microsoft Expression Web Designer

我暂时不知道Microsoft的Expression Web DesignerOffice Sharepoint Designer有什么不同,对比过它们的安装选项看起来是一样的,所以暂时当它们一样吧,我现在正在使用的其实是Office Sharepoint Designer。

我最近做了一些小的CSS Template设计,就是设计出来的结果是一整个demo页面,里面已经包含各式常用的HTML元素与元素组合,并且是styled好的,让人一看上去就知道使用这套CSS Template制作出来的网站会是什么风格,同时使用者还可以根据自己的需要添加和覆盖一些CSS规则,简单来说就是类似Mollio那样的CSS Template。对我来说,Office Sharepoint Designer极为方便的地方包括:

  • 元素margin与padding的可视化,让你无需专门改动CSS就能方便看到页面上元素所占据的margin和padding。
  • CSS继承链的可视化,选择一个元素后CSS窗口选择“摘要”模式,就能显示它一路继承下来的属性,其中已经被覆盖掉的属性会用删除线标记。

有了这两项支持,设计就变得极为方便了,设计一个CSS Template只需要以下几步:

  • 用完全只考虑语义的方式,将需要styling的HTML Template写好,通常这个HTML Template包括常用的元素组合。
  • 自顶而下地进行CSS定义,在Office Sharepoint Designer中可以直接看到效果,用一个比较符合标准的浏览器测试也行。
  • 当整个CSS都定义完了,再考虑用不太标准的浏览器进行测试,并添加相应的修正项。

值得注意的是,Office Sharepoint Designer自身显示HTML可视化设计界面的解释器与IE6或IE7都是不同的(其实以前Frontpage也是独立解释器),它比IE6更符合标准,支持CSS也非常好,但实际上它解释出来的效果有可能会和IE7略有差异,所以千万不要以可视化界面中的效果为准。

ASP.NET 无法确保在注册的 JavaScript 内不存在重复定义

在ASP.NET 2.0中,我们使用Page.ClientScript属性(也就是一个ClientScriptManager对象)的一些名字以Register开头的方法注册客户端脚本,这是大家都知道的。

理论上应该如何避免冲突

先说说为什么要这样注册脚本,而不用Response.Write直接输出。举个例子,你用3个DropDownList做了一个输入日期的区域,分别代表年/月/日,然后你为了防止用户输入2007/02/31,所以你决定把这3个DropDownList做成级联的,也就是随着年和月的输入改变,日的可选项跟着改变。这时候你可以通过写一些JavaScript来实现级联,例如定义一个名为updateDateRange()的JavaScript函数负责更新DropDownList,然后直接把这些JavaScript放到C#的字符串里,并且使用Response.Write输出。这些代码用起来会很正常,直到有一次你的页面需要输入两个日期。

在需要输入两个日期的那个页面上,你把3个DropDownList复制粘贴了一遍,也把输出的JavaScript的那段代码复制粘贴了一遍,接着根据两处ID的不同做了相应的修改,结果有一组级联无法正常运行起来。你查看服务器端输出的HTML,接着恍然大悟——原来有两个updateDateRange()函数。于是你把updateDateRange()改为updateDateRange(yearControlClientId, monthControlClientId, dateControlClientId),同时把JavaScript删减为仅输出一遍,这时候无论哪组级联都使用同一个函数,它们根据调用时输入的DropDownList.ClientID来区分。

又有一天,你决定把这组级联封装为一个UserControl,做起来当然还是复制粘贴大法,也就是把3个DropDownList和JavaScript复制进UserControl,然后把UserControl的引用复制回原本的调用处。忙完之后,发现那个有两个日期输入的页面又出错了,原来updateDateRange(yearControlClientId, monthControlClientId, dateControlClientId)又被重复输出了,因为页面上放入了两个UserControl所以JavaScript被输出了两遍,并且没办法减少输出次数。

这时候你可以使用Page.ClientScript.RegisterClientScriptBlock解决问题,它通过type和key这两个参数确定脚本是否被重复注册,而被重复注册的脚本仅会输出一次。为什么要type和key两个参数呢?以前ASP.NET 1.x的同类函数只有key一个参数,这带来的问题是可能两个不同的控件设计时都使用了同一个key来注册自己的脚本,结果其中一个控件脚本的成功输出必然会抑制另一个控件脚本的输出。加上了type参数,各控件都用自己的类型作为标识,这样就能有效避免注册时冲突。

为何无法真正避免冲突

关于这个问题,我们先看看ASP.NET内部定义的JavaScript是以什么方式命名的。通常,private的全局函数或变量,命名都以双下划线开头,例如大家熟悉的__doPostBack,或者是WebPartManager在客户端使用的__wpm。而public或protected的全局函数或变量,一般就好像C#那样使用Pascal命名法。具体的例子,大家可以用Reflector看看System.Web.UI的资源中的那些js文件。

我们暂时就假设这种命名法是正确的,然后模仿着去在自己开发的控件中实践。事实上很多控件开发者也确实这样做了,比较多的专业控件中你都能看到双下划线开头命名的函数或变量,这至少可以避免和控件使用者在页面上注册的函数或变量冲突,因为在页面上注册的函数或变量通常都采用比较简单的命名法。

假如现在我们要做一个浮动上下文菜单,也就是当你的鼠标移动到某个HTML元素上时该浮动菜单自动出现,当鼠标离开元素并且也不在菜单上时,菜单自动消失。为了方便用户操作,我们允许用户鼠标移动过程中稍微离开菜单区域,所以定义当鼠标离开菜单区域若干时间后才让菜单消失,而这个时间在客户端保存在__disappearAfter变量中。这个控件看起来什么问题都没有,直到你把它和ASP.NET 2.0自带的Menu控件放在同一个页面上,因为Menu控件也有类似的功能,而且和我们的控件一样Menu控件选择了将时间变量保存在一个名为__disappearAfter的变量中。

现然,作为ASP.NET框架的使用者,框架没有声明这个变量的名字不允许使用,我用了有问题当然就可以认为是框架的错。Brad Abrams写了一本《.NET设计规范/Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries》,里面却完全没有提及JavaScript和CSS的规范,好像在ASP.NET中使用到的JavaScript与CSS都是琐碎的不能在琐碎的事情,所以完全不值得一提。

事实上,既然ASP.NET允许一个页面上不同的控件设计者引入不同的JavaScript和CSS,就必须提供一种方法去管理潜在的命名冲突。如果是JavaScript,我们可以考虑使用ASP.NET AJAX的namespace来避免冲突,下一代代号为Orcas的Visual Studio和ASP.NET将内置ASP.NET AJAX支持,所以其内置控件所使用的JavaScript应该也会有namespace,这样就有有效降低冲突概率。至于CSS命名冲突,暂时没有好的解决放案,只能依赖控件设计者的习惯了,你可以考虑为你的控件根元素附上一个namespace以示区分,这样也算是降低冲突概率的一个办法。

最后,如果你希望阅读更多类似的ASP.NET主题文章的话,欢迎订阅Cat in dotNET (Feed: http://feeds.feedburner.com/CatChen/dotNET)或Cat in Chinese (Feed: http://feeds.feedburner.com/CatChen/Chinese)。

2007年1月25日星期四

放假了,要去做个小手术

又放假啦,又不知道做什么好了,暂时没有任何计划。

唯一问题是下星期一要做个小手术,有人愿意之后来探望我的话,可以联系我哦。

2007年1月14日星期日

开始学习 Cantonese Typing

对住份粤语字打法大全(2007贺岁版)慢慢睇,都几恶顶下,考虑係咪开翻个Cat in Cantonese玩下先。

Microsoft 应该学学 Sony 的“产品皆无完美”设计思想

MS现在推广Vista的最大障碍应该就是XP。虽然XP+SP2已经比较臃肿,但还是很够用的,XP潜在安全性问题虽然不少,不过既然SP2通过安全中心加上了那么多安全方面的提醒,也就够了。

Vista推广自己足够安全,然而却拿不出什么实际的例子来说明它比XP更安全——你总不能通过揭露自己一个产品的短处来推销自己的另一个产品吧。例如Vista的那个Virgin Stack,投入了去写一个全新的TCP协议栈,而放弃从原有系统的成熟代码修改而来,这点就应该拿出来卖卖广告啦,可惜至今只有安全领域的专业人士知道这个实事,大众用户对此一无所知。

Sony的聪明之处在于产品从开始设计时就注定是不完美的。Sony只有非常少数的经典产品可以称之为完美,大多数产品就算能够成为经典,也一定留下一个巨大的不足,或者说是瓶颈,让用户感到不够用,从而乐意掏钱买下一代的产品。

最明显的例子是Sony CLIE的TH55拔牙事件。在TH55上市之前,Sony官方的页面都是暗示这台PDA将成为完美机型的——蓝牙、Wifi、480*320长屏、摄像头、CLIE升级过的Organizer软件、Sony诱人的工业设计。然而就在TH55正式发布的前一个晚上,原来的官方页面被撤了下来,新换上去的页面公布这台机没有蓝牙,这导致大批的CLIE Fans愤愤,最终调解的结果是只有欧版的TH55是有蓝牙的。

TH55拔牙事件说明了Sony的产品设计思想,它就是不愿意看到有一个机型能够满足大多数用户的所有要求,然后人们买到这台机后就不再升级了。即使到了发布前的最后一个晚上,Sony也要改变主意把TH55的蓝牙拔掉,从而让买了TH55的人以后为了蓝牙功能而更新换代。

XP现在的问题就在于它已经太好用了,这无法促成用户去更新换代。或许MS也在祈求多一些XP有而Vista没有的安全漏洞出现吧,这样它就可以暗示大家为了安全所需而升级到Vista了。

2007年1月13日星期六

我给 Google China 出主意

看到这个活动,我又参加了,嘻嘻……先说明,我觉得这个活动的题目应该标明是Google China,而非Google,它们不是同一回事,而是从属关系的两个实体。

2007年的Google China,是时候推出一些收费服务啦,要在中国从个人口袋里挖得到钱的服务。这是不是个非常违反常理的建议?正所谓“索取比给予还要好”(参考《硬球/Hardball》),如果你的用户已经愿意为你的服务而掏钱,那么还有什么他不愿意为你干的?

分享价值是Google价值观的一部分,然而分享方式确实非常美式的——我放在这里啦,你要的话你可以来拿啊,我不会强迫你要的。但这在中国是行不通的,中国人习惯了作客时主人会拿一大堆好吃的出来——你吃这个啦,这个好吃啊。当然,给你吃不是白吃的,如此热情好客的背后,当然是希望拉拢关系。甚至好像婚礼这样的,给你请贴了你不能不去,去吃了不能不封利事。这听起来很强买强卖,但中国人就习惯这样,因为不是这样半推半就的接受而是主动请求获取,反而是显得不礼貌的。

这样说来,流氓式的软件或者服务才真正体现了中国文化的精髓(或者是糟粕)。你想要,但又不好意思说,是不是?那我就硬塞给你,给个充分的理由让你接受,如此你就不拥顾虑别人在你背后怎么说你了。Google要学的就是这个,给个充足的理由用户去接受你的服务,这个理由不一定是经济上的,可以是道德上的。

“你看人家Google都把服务送到你嘴前了,你不张一张嘴,这不太合乎人情吧?”大多数人都会选择张嘴,而不是带着“不近人情”这顶帽子。好像Google这样优秀的服务,只要成功让你的用户吃上一口,后面他自然会主动要。而如果好像Tencent那样的烂服务,就每一口都要这样强迫用户去吃。

用户乐意吃了,这时候就应该收钱了。收多少无所谓,反正Google不缺这些钱,需要的就是获得用户的投票——如果你能让一个中国用户自愿给钱你,也就得到了他对你足够的信任和肯定。之后,你无论推出什么服务,这些信任你的用户总会乐意去用的。

2007年1月12日星期五

回归 Blog*Spot

现在Blog*Spot已经有一段时间没被封了,而地震之后Sitesled一直上不了,现在连Blogger也无法发布到Sitesled了,所以只好转回Blog*Spot。

转会去之后,自然考虑使用新的Layout模式,而放弃旧的Template,所以界面看起来会不同了,有些以前有的功能也暂时取消了。不过大家可以放心,这些功能很快会恢复,我考完试了就会把Layout改好,让它支持更多更好的功能。