2008年8月30日星期六

如何动态加载 JavaScript 与 CSS

Omar AL Zabir这位MVP总是喜欢搞些稀奇古怪同时又很实用的小东西,并且还十分值得参考。最近他就做了一个叫做ensure的小工具用于动态加载JavaScript、CSS与HTML,而且IE、Firefox、Opera、Safari都支持了,那么我们就来看看ensure是如何做到动态加载JavaScript与CSS的。

在介绍ensure内部的实现之前,让我们先来看看其功能:

ensure({
    html: "popup.html",
    javascript: "popup.js",
    css: "popup.css"
  }, function() {
    Popup.show("hello world");
  }
);

在这段代码中,ensure首先会确保popup.html、popup.js、popup.css这3个文件的加载,如果都没加载过ensure就会动态加载它们;如果已经加载过了,ensure不会再次加载。在确保这3个文件都加载后,ensure会调用后面的匿名函数,也就是执行Popup.show("hello world");。

接下来,就让我们看看ensure是如何动态加载JavaScript与CSS的。

加载JavaScript

在ensure当中,加载JavaScript分两种情况来执行,也就是Safari与非Safari这两种情况。

在IE、Firefox、Opera中加载JavaScript

在这三款浏览器中加载JavaScript,其实只需要创建一个script元素,把src指向要加载的URL,最后把script元素追加到head元素上,那就搞掂了。此项工作是在HttpLibrary.createScriptTag()中完成的。不过我们不仅仅要加载JavaScript,同时还需要知道它什么时候完成加载,这可以通过script元素的onload事件或onreadystatechange事件来实现。

在Safari中加载JavaScript

因为Safari 2不支持onload或者onreadystatechange,所以只能手动通过XHR把URL读去过来,然后再手动eval这段代码,这就带来了一个限制──只能加载本域的JavaScript文件。在ensure当中,eval的工作是通过HttpLibrary.globalEval()来完成的。为了让JavaScript代码在全局(global)上下文中eval,ensure还是使用了创建script元素的方法,并将要eval的JavaScript置于其内,最后把script元素追加到head元素内。

细心的人肯定要问,为什么HttpLibrary.globalEval()要如此设计,而非直接window.eval或者eval.call。这是因为,window.evaleval.call都无法在IE6中实现和script标签加载JavaScript代码一模一样的效果,这两种做法的eval在IE6下仍然不是在全局上下文中执行的。搜索一下你就会发现一些相关的讨论,例如jQuery就曾经使用window.execScript()来完成此项任务。不过最终大家都发现添加script元素才是最好的跨浏览器解决方案,所以现在的jQuery和ensure都是如此实现的了。

加载CSS

相对于加载JavaScript而言,加载CSS就简单多了,而且方法也是类似的:在head元素内直接加入link元素就可以了。这也正是loadCSS()所完成的工作。

实际上,ensure没有确保CSS完成加载后再执行下去。这估计是因为浏览器都能够在CSS加载完成后自动应用到页面上,因此Omar AL Zabir就认为CSS的加载顺序是无关紧要的,不过假如CSS加载速度实在太慢,其实还是会影响显示效果的。

在IE6中加载CSS

这次需要特别照顾的是IE6,而非Safari。IE6在往head元素添加link元素时,必须在window的上下文中完成,因此添加link的函数通过call调用切换了上下文。

总结

实际上动态加载JavaScript与CSS都并不难,在大多数情况下只需要向head元素追加对应的子元素就可以了,只有Safari2和IE6这两款古老的浏览器是需要特殊照顾的。

2008年8月29日星期五

开始使用 MarsEdit 写 blog

缺少一个客户端,我就总是懒得打开Blogger来写blog。当然,Blogger还算好的,我可以写着写着就按一下save按钮,然后继续写。博客园还不能就地保存,保存一下必须离开编辑页,然后还要重新打开编辑页。我实在无法忍受从PC切换到Mac之后没有客户端的情况……其实是有过一段时间用客户端的,那就是MarsEdit

在MarsEdit的试用期过后,我又懒得写blog了。在忍受不写blog与购买MarsEdit之间,最终我选择了购买MarsEdit,这个价值$29.95还周围都找不到coupon的东西。作为一个shareware,它真的做得不错,但$29.95又实在太贵了,Speed Download那么强大的下载软件也就$25嘛。

在买MarsEdit时,发现可以用PayPal,想起之前注册PayPal时验证信用卡后有$1.95存在PayPal上面了,决定就用PayPal支付,把那$1.95余额花掉。接着在PayPal的页面看到,原来通过PayPal来购买外汇超级是超级贵的。根据今天Yahoo Finance公布的汇率,1CNY能够买入0.1459USD,但是PayPal说1CNY只能买入0.141154USD,实在相差太远了。幸好,PayPal允许你选择不用它来做购汇,用回VISA的购汇功能,只不过支付时无法确定最终购汇汇率。如果VISA的购汇汇率不至于那么离谱,还是VISA比较省钱。不过到底VISA购汇价格如何,我还真的没有留意过,以后要仔细研究一下帐单。

2008年8月20日星期三

MobileMe 真应该免费 beta 一年

服务上线两个星期,就因为服务质量问题表示道歉,每人送30天试用期。然后因为港币用户的验证费用扣错了(应该扣1USD,实际扣了100HK$),又再送30天试用期。现在又因为近期服务不稳定,再给大家60天试用期。加上注册时的60天试用期,这已经是180天了。

对比起这样不断道歉和延长试用期的做法,Apple还不如学习一下Google,让服务免费beta,让更多用户来发现bug和汇报bug,等服务成熟了再开始收费。当然,这样做的坏处是Apple必须保留免费用户,否则全面转为收费的话,很多beta阶段的免费用户都会流失掉。

2008年7月29日星期二

北京地铁比广州地铁落后多少年?

星期六跑去三里屯看了一下“中国第一家Apple Store(非Apple Authorized Reseller)”,是搭最近开通的10号线过去的,感受了一下10号线的整体装修,觉得能够比得上广州3号线、4号线的水平,对北京地铁略微有改观。非常可惜的是,10号线全线没有手机信号覆盖,我觉得这实在是太落后了。

据说以前北京1号线和2号线也是没有信号覆盖的,但是后来改进了。10号线作为全新开通的线路,应该在设计阶段就考虑到这个问题,然而现实却是正好相反。想想10年前广州1号线开通,就已经解决了这个问题,而北京现在开一条10号线,还要是迎奥运了,都解决不了这样一个问题,只能说北京地铁比广州地铁要落后10年。

北京地铁还有一个搞笑的地方,就是所谓的“自觉安检”,安检不是放在必经通道上,必经通道旁边,人少的时候安检人员还能认出谁的包包经过机检了,人多的时候根本分不清,只听到安检人员说“请自觉打开包包接受检查”,很多人根本就不理,直接走过去了。还有一个搞笑的地方是,Singzy说她早上赶去机场时,本来想坐机场线,结果地铁安检说他们的行李体积过大,不能带进地铁──竟然航空公司能够接受的行李体积,地铁安检不能接受,那么北京赶在奥运之前开通机场线又是为什么呢?貌似机场线还有一列迎宾专列,估计就是专门用来迎宾的吧。

2008年7月5日星期六

去给韶关移动做讲座

Bean之前一直说去给移动做讲座的事情,说要讲讲互联网的新玩法新思维,最终在这个星期五去给韶关移动做了第一讲。

玩转互联网信息沙龙

这次我们讲的是购物和SNS这两块,Bean负责讲购物,而我负责讲SNS。我看了一眼听众名单,发现来的都是韶关移动的老总,至少都是部门经理级别的,第一次做这样界别的讲座,我当然紧张。Bean说,要讲得简单一点,避免涉及技术,让他们明白怎样好玩就行了。所以我准备的内容,更多是介绍性的,而不涉及更深层次的问题。最终讲完,得到的反馈还算不错,不过我想他们还是希望多听听关于商业模式的东西。移动很有野心,想要做自己的互联网平台,从而避免自己最重成为纯粹的互联网访问介质,因此移动的老总们都很关心现在的互联网思维模式。

另外,这是我第二次尝试用Apple的Keynote做演示,当然想要做得比第一次好,让画面和动画效果更加深入人心。感觉无论是PowerPoint还是Keynote,其实质都是差不多的,最重要还是表现手法,软件只是工具而已。

2008年6月29日星期日

Non-Catonese Speaker 取个 Cantonese 的名字干什么呢

时不时就能在网上认识到一些non-Cantonese speaker,却发现他们用的英文id取个港式音标,例如姓杨的取Young,姓蔡的取Choi,实在搞笑。

事实上,港式音标是威妥瑪拼音,并且是按照粤语发音来注的威妥瑪拼音。如果是non-Cantonese speaker,那么你可以用国语或者任何你所说的汉语口语方言来注威妥瑪拼音,但没必要用基于粤语的威妥瑪拼音。举个实际的例子,蔡依林的英文名是Jolin Tsai,就是按照国语发音来取姓氏的威妥瑪拼音,如果她按照粤语发音来取Jolin Choi,那就显得很不伦不类了。

因此,如果你不想用汉语拼音来给自己的中文名字取个拉丁形态,那么你可以选择威妥瑪拼音,但不要盲目选用基于粤语的威妥瑪拼音,假如你是non-Cantonese speaker的话。

2008年6月26日星期四

Purpose and Reality of Test

测试的目的是为了挑选,例如挑选高质量的人,然而这可能会成为一种反向激励──应试。这会导致候选人偏离测试所期望达到的质量,甚至出现劣币驱逐良币的景象。

还记得和状元同学开过的一个玩笑。由于某些琐碎事情,我讽刺他说,“做人怎么能如此不求甚解呢?”他说,“有什么不可以的,我就跟着老师说是正确的去做,我也不知道为什么这样做是对的,不求甚解当然是可以的。”当时我就把这当作一个玩笑,现在我明白这是十分正确的一件事,而且这就是反向激励的实现形式──为了应试,你必须牺牲测试所期望达到的质量,以换取应试能力。

因此,现实中的测试不一定真正能够挑选到高质量的人,而且还把人赶往了一个相反的方向。

2008年5月31日星期六

Social Software Configuration

social software满街都是,连一个最普通的论坛也算一个social software,但是social software最难的部分在于social而不在于software。

一个software可以有完备的需求分析,在很清楚我们要解决怎样的问题以及如何解决之后,再去实现这个software。然而social则是动态的,当一个social software投入运行后,social的部分才开始“实现”。举个例子来说,你想从零开始做一个博客园,software方面你可以从任何一个blog platform开始修改,甚至是从零开始编写,但是social方面呢?你明白你要实现的效果是一个有人气的技术博客社区,但随后没有OOAD这样的理论模型让你把这变成一组描述social模型的规则。虽然你知道要去吸引潜在用户、激励用户多写好的文章……但是这一切都仅仅是经验列成的规则。

随后,软件平台做好了,意味着software的编码与测试阶段都完成了,但是对social部分的实现才刚刚开始──用户逐步进驻,他们之间开始交互,而你则通过设置各种各样的规则来刺激整个social氛围朝你期望的方向发展。这个过程不像是开发,反而更像是测试于调试──首先,你测试现在的social氛围是否符合你的要求了,例如用户数量够不够,他们发文质量高不高;然后你找到表面上不满意的地方,对底层的规则进行修改,这些规则有可能是显而易见的,也可能是需要深度挖掘才能弄清楚的;如果要深度挖掘,那就成了调试了,在密密麻麻的规则当中尝试修改一下,然后看看效果,不行再改回来然后再做下一个尝试。

总之,social software拥有比一般software长得多的研发周期,假如把social部分也算上的话。并且最重要的是,每一次social开发的迭代都没有严格的方法论可言,大部分人都是通过经验来进行控制的。那么到底现在有哪些经验可以参考,这个以后再说。

2008年5月24日星期六

原来并非人人能习惯中英混杂

在Microsoft 2008 Launch广州站的会议结束后,我和Bean、黎波、张亮去了中酒旁边的McDonald's聊天。我说到“这是100%确定的”,读出来是“这是一百percent确定的”,然后黎波马上就问“你说一百什么”,Bean解释道“一百percent啊,就是百分之一白”。随后Bean又补了一句,“和Cat聊天聊多了你就能习惯这种中英混杂的说法了”。这时候我才突然发现,原来不是所有人都天生能适应中英混杂的说法的,即使他中文和英文水平都没问题。

随后,我又在豆瓣的搭讪小组碰到了一些更加negative的反馈,我才开始仔细分析这个问题。这应该属于文化背景的差异,港台文化都是习惯中英混杂的,geek自然也常常用英文,因为我属于上述两个圈子,自然我很习惯这样说话。但是对另外一些人来说,他们从小长大的文化背景中不包含英语这一元素,甚至在英语学习中遇到了种种障碍导致厌恶情绪的产生,这时候他们不仅仅不适应中英混杂的内容,甚至就认定你使用英文就是在炫耀你的英文能力。

廣東話俗語——雞髀打人牙骱軟

本文由Piggest授权转发

“雞髀”——鸡腿;“牙骱”——牙关(jaw, jawbone)。


整句合起来的“繁→简”机翻是:鸡腿打人牙关软。

在文字MUD年代,每每看见“鸡腿”这件武器/补给品,可以吃又可以打人,不由得想起“雞髀打人牙骱軟”这句俗语。用鸡腿打人到底可以达到什么效果呢?

“雞髀打人牙骱軟”的意思是,予之以小利,使对方对自己透露消息。有时亦指,予之以小利,使对方为自己美言几句或者在言语上偏袒自己。

例句:咁着数,唔通想“雞髀打人牙骱軟”?

“雞髀打人”实在太生动,所以这句话又广泛应用于以下情境:

为了获取利益或求人帮忙而请人吃饭;政府派利市拢人心;甚至在办公室派零食讨好同事亦算有“雞髀打人”的嫌疑。

可见得“雞髀打人”是中国人常用的社交手段,不含褒贬,是大家都能会心一笑了然于心的潜规则。

引申:“髀”即小腿,“大髀”即大腿。“打牙骱”——chip chat。

以上粤语发音,请见“粤语会馆”:http://www.yueyu.net/

2008年4月16日星期三

飞到美国了

离境的时候,觉得北京首都机场的国际安检很弱智。首先,在明明没有人排队的情况下,就把长蛇阵拉起来了,这证明排队从来没被很好地管理过。国外的长蛇阵,甚至是广州的各大银行里的,都是先放好若干横排,但是留两边的直行通道,随着排队人数增加,再把通道逐步封闭,这样排队就转到横排里了。在北京机场,根本没有直行通道这回事,再少人也要绕啊绕啊绕,太过弱智了。

过安检门的时候,首先要看登机牌,我们是自助打印登机牌的,一开始安检人员又看不懂这是否是合法通行证,又要确认一下。因为我们是去美国的,特意要求我们要脱鞋,也就是去其它国家可能不用脱鞋检查啦?那就是要根据机票来决定安检严谨程度啦?这样效率不低才怪呢。现在在登机之前,还有一个液体物品的检查,也就相当于两次安检了。我们到得很早,见到第二次检查的人由队长带着来打登机口前,然后一轮训话,说今天开始严查,所以大家不能随便开包看看问问就行,一定要认真查。结果呢?开包之后,还是很随便看看就行了。

在美国入境时,又是长蛇阵的排队,接着是入境面谈,问了一下来干什么的和要停留多久。开头我在海关申报单选了非business,结果入境官员说,别人付钱让你来的就是business,我说那就business吧。我可从来没想过,作为一个学生,有人出钱让我来开会,这也叫做business。在领到托运行李后,又被人“随机”扔去了过X光检查,真是不幸,不过X光检查的黑人倒好人,看到我是到Microsoft的,就很欢迎的样子,可能最近几天到Microsoft的人太多了,而Microsoft又是Seattle的支柱吧。我的箱子有北京托运来的标记,他就问我北京是不是在紧张准备奥运。

之后就出去了,就这么简单。当然,也有朋友被拦下来的,估计因为他在日本买了个新相机,然后未出关就在那里狂拍照,哈哈。

2008年4月12日星期六

黄金周

记得某几个学期,课非常少,一个星期就上两三天课,于是我们说“周周都是黄金周”。其实怎样才算是黄金周呢?我觉得你要是数着日子过,每一天都是回忆的,那才是黄金周。在大学里天天睡觉,又不记得梦见什么,上网太多导致时空感错落,这显然不是黄金周。

明天就飞西雅图了,非常向往,但也非常留恋在北京的不到5天。为什么呢?因为这5天很早就计划好了,从第1天开始你就是倒数着过,每天很清楚记得自己干过什么,因此印象特别深刻。举个例子,同样是在北京,我在百度实习3个月,印象就要浅一些,大概只记得第几周做过什么。这不是因为上班的问题,而是因为我站在第1天向前看就是12周,按周来倒数。如果我在百度的实习是5天,那么我也会清晰记住每天干了什么。

因此,黄金周在于日子倒数着过,而最好的方式就是去旅行。在北京这5天没有怎么出去玩,明天飞到西雅图一定要抓住唯一大家都有空外出的一天,去爽一下!另外期待暑假的德国之旅,如果能去成的话。

2008年4月11日星期五

广州地铁三号线的算术陷阱

三号线作了一个奇怪的设计,就是一开始的列车只有三节,然后说将来要扩容了再加车厢。加车厢的问题在于,一旦要加一节车厢,现有的列车都必须同时加一节,这样才能确保开闭的屏蔽门是统一的。否则一列短车一列长车的话,乘客在长车车厢位置候车,发现来的是短车,他正对着的那个屏蔽门不开,那就很郁闷了。

这种设计,鼓励的是加独立的列车,而不是加车厢。然而,列车加得越多,加一节车厢的成本也就越高,因为同时要加一节车厢的列车数多了。这就变成了,列车越多,越不鼓励加车厢,越倾向于再加独立的列车。最终,整条线路已经无法再加新的列车时,列出数肯定已经相当多了,这时候加一节车厢的成本就会很大,因此加车厢的可能性反而很低。

我觉得唯一的可能就是,在列车很多的时候选择把短列车重新分组为长列车,多余的机车没所谓,反正机车肯定有冗余的,这样子才能避开这个算术陷阱。

2008年4月2日星期三

救救 Web Developers ,拒绝 IE6 !

这是最近的一场于web developers相关的campaign,官方站点是SaveTheDevelopes.org。在上面你可以下载一段脚本和对应的图片,放到你自己的网站上,然后只要有用户使用IE6访问你的网站,他就会看到一个小小的提示框,建议他升级到IE7或选用其它非IE浏览器。当然,你也可以直接引用SaveTheDevelopers.org上面的脚本文件,但大家都知道潜在的风险,因此最好不要这样做,如果你有自己的空间的话最好还是下载下来放到自己的空间上。

为了支持这个campaign,我特意为他们做了一个中文翻译。我本来是想和aw一起做的,不过他们把内容发给我的那个晚上找不到aw,于是有翻译问题就直接找Junyu问了,最后在Junyu的帮忙下把翻译搞定了。我在想是不是应该多做一个粤语翻译,这样会比较好玩,反正也就一个晚上的事情而已。

2008年3月24日星期一

深入理解 ASP.NET 动态控件 (Part 6 - 模板控件)

在之前的文章中,我极力推荐大家使用Repeater和MultiView这类TemplateControl,为什么呢?因为只有这样做,才算是符合MVP或MVC模式。(到底是MVP还是MVC,这视乎你选用什么呈现引擎了。)

虽然我们要动态创建控件,但实际上这部分控件仍然属于View的部分,我们应该尽量采用ASPX的声明性名义来描述这些控件,避免用C#代码来创建控件、设置属性并添加为子控件。就拿最简单的例子来说,创建一个LinkButton,通常我们都需要设置它的ID、Text、OnClick属性/事件,甚至还要设置OnCommand、CommandName、CommandArgument等属性/事件,那就是大概3到5个属性了,用ASPX来声明只需要1~2行代码,而用C#代码则需要写至少5行(把new和Add()也算上的话),由此可见在定义控件这类声明上ASPX比C#代码的可读性要高。

接下来,我们来研究一下TemplateControl是如何工作的,这自然要从如何编写一个TemplateControl讲起。

编写模板控件

在这里,我们假设要编写一个SimpleRepeater控件,自身不支持数据绑定,只有唯一一个名为ItemTemplate的模板,并且就按照Count属性指定的次数重复出现该模板。首先,让我们来定义这两个属性:
public ITemplate ItemTemplate { get; set; }
public int Count { get; set; }

因为ItemTemplate属性不是以键值对的形式在SimpleRepeater的声明中给出的,而是以内嵌一对标签的方式定义的,因此我们需要让解释器去把<ItemTemplate>...</ItemTemplate>中间的内容读取出来,并把解释结果作为ItemTemplate属性的值处理。这时候,我们就需要为SimpleRepeater类加上ParseChildrenAttribute,也就是这样子:
[ParseChildren(true)] public class SimpleRepeater {...}

最后,我们需要重载一下CreateChildControl()方法,把ItemTemplate的内容作为子控件添加到SimpleRepeater之内:
protected override void CreateChildControls()
{
if (ItemTemplate != null)
{
Controls.Clear();
for (int i = 0; i < Count; i++)
{
Control control = new Control();
ItemTemplate.InstantiateIn(control);
Controls.Add(control);
}
}
}

这段代码的用意应该是相当清晰的了,就是循环Count指定那么多次,每次循环创建一个空白的Control,用ItemTemplate.InstantiateIn()方法填充它,最后把它添加到SimpleRepeater.Controls里面,就那么简单。那么这个神秘的InstantiateIn()方法到底是干什么的呢?后面来解释。

编译模板控件

在之前的《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》里面,我详细地解释了ASP.NET 2.0的编译模型。在《深入理解 ASP.NET 动态控件 (Part 5 - 编译实验)》中,我们又做了一个动手实验,亲眼看到了ASPX和C#代码是如何编译到一起的。现在让我们来看看当碰上模板控件时,代码会被如何编译吧。我们把上面编写的SimpleRepeater注册到页面上,前缀为ctrl,并且编写如下一段代码:
<ctrl:SimpleRepeater ID="SimpleRepeater1" Count="10" runat="server">
<ItemTemplate>
<asp:Button ID="Button1" Text="Button" runat="server" />
</ItemTemplate>
</ctrl:SimpleRepeater>

然后还是用aspnet_compiler编译一下,并且用Reflector打开编译出来的dll看看。我们可以看到构造SimpleRepeater实例是通过这样一个语句完成的:
return new SimpleRepeater { ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control1)), Count = 10 };

这个语句其实就是一个普通的new语句,并且给ItemTemplate和Count两个属性赋值了,唯一值得关注的就是ItemTemplate的值。ItemTemplate的类型是ITemplate,因此任何实现了ITemplate接口的类都可以复制给它,然而我们却从来没指定过到底要赋值什么类型的实例给它,因为这已经由ASP.NET帮我们想好了,假若我们不指定的话,那就是CompiledTemplateBuilder类型。还是熟悉的Builder模式,它只需要已委托的形式接受一个实例化ITemplate的函数,然后就能返回实例化好的ITemplate控件子树。可能你会问,既然我已经有实例化ITemplate的函数,干什么要先传给你CompiledTemplateBuilder,让你来调用一下,再把实例化好的给我?我自己实例化不好吗?在此,ASP.NET引擎的做法只是为了保持Builder模式的一致性,处处用Builder模式来分离逻辑而已。

那么这个用于实例化ITemplate的函数从哪来呢?在解释器进入到<ItemTemplate>...</ItemTemplate>内部时,它会继续层层构建Builder模式,就如同在整个页面内执行的一样。因此,整个<ItemTemplate>...</ItemTemplate>会被解释器转化为一个函数,它也是通过层层调用内部函数完成自身的控件子树的构建,传递给BuilderTemplateBuilder构造函数的委托正是指向此函数。

因此,模板控件里面的内容将如同模板外的内容一样,被无缝地解释和构建到一起来。

INamingContainer

如果你查看SimpleRepeater输出的HTML代码,你会发现里面有10个<input id="Button1" name="Button1" type="button" value="Button" />。我们都知道,重复的id是不符合标准的,因此我们需要通过INamingContainer把这个问题解决掉。因为重复的控件是ITemplate,所以应该对它加上INamingContainer,然而它的实例编译时自动使用了CompiledTemplateBuilder,我们如何把INamingContainer加上去呢?我们就只能把INamingContainer加到它的父控件上面去。此时,我们需要一个实现了INamingContainer的简单控件:
public class SimpleRepeaterItem : System.Web.UI.Control, System.Web.UI.INamingContainer {}

然后我们把SimpleRepeater.CreateChildControls()方法的这个语句:Control control = new Control(),替换为:SimpleRepeaterItem control = new SimpleRepeaterItem()。这样,ITemplate的容器就变成了一个具有INamingContainer接口的控件,这时候各个Button的客户端id就会自动加上其容器的id作为前缀,因为容器的服务器端ID是自动变好的,所以必然是各不相同的,这样就解决了Button客户端id相同的问题。

通过这个例子,我们了解到了编写模板控件时必须为模板的容器加上INamingContainer,因为模板内的控件ID命名是可能重复的,加上INamingContainer就可以避免它们的客户端id重复。

小结

这次的文章解释了为什么我们应该尽量使用模板控件来实现动态控件,并且也说明了如何编写自己的模板控件,以及模板控件最终是被如何编译为Builder模式的代码的。

2008年3月21日星期五

每个 blog 都会有一堆 draft posts

lulu说她有个blog post选题的文件夹,我也有一堆blog post都draft存放于各处,可能是邮箱中,可能是Blogger的草稿中,也可能是Windows Live Writer等软件的草稿中。通常,一篇写得好的长文章都不是即兴而作的,而是用draft慢慢写出来的。有点点feel的时候,要逼着自己写成一篇完整的文章很难,反而当有几个draft都是同一个相近的题目时,可以合并在一起写成一篇好文章。

这估计是因为,如果只是有一点灵感,这是不全面的,很难自圆其说。当几个灵感合并在一起,就能看到概貌了,前因后果清晰了,正方反方都出场了,自然写出来的文章就会显得内容丰富一些。

深入理解 ASP.NET 动态控件 (Part 5 - 编译实验)

这次的文章是一个小小的动手实验,你需要准备好Visual Studio 2005或者Visual Studio 2008,以及最新版本的Reflector。通过这次的实验,你将对ASPX与C#代码如何合并编译为一个dll代码有所理解。

在实验开始之前,首先来一个小问题:如果不允许你使用ASPX,要你完全使用C#代码写一个具备复杂控件树的页面你会怎么写?把声明控件的代码都放在Page_Load里面吗?或者有更好的代码编写方法?先想想这个问题,然后继续往下看。

实验的第一步,也就是在Visual Studio里面创建一个ASP.NET项目,并编写一个简单的ASPX页面。例如下面这个例子:(以下代码仅包括HtmlForm内的主体部分)
<asp:MultiView ID="MultiView1" runat="server">
  <asp:View ID="View1" runat="server">
    <div>Please choose either of the followings:</div>
    <asp:RadioButton ID="RadioButton1" runat="server" />
    <asp:RadioButton ID="RadioButton2" runat="server" />
  </asp:View>
  <asp:View ID="View2" runat="server">
    <div>Please choose any of the following:</div>
    <asp:CheckBox ID="CheckBox1" runat="server" />
    <asp:CheckBox ID="CheckBox2" runat="server" />
  </asp:View>
</asp:MultiView>

在这个例子中,我们构建了一个简单的控件树,同时又不至于过于复杂,确保了编译出来的代码相对简单一些。接下来我们就需要将它编译了,最简单的手动编译方法就是用ASP.NET 2.0自带的aspnet_compiler.exe,这个文件默认会在这个目录中:C:\Windows\Microsoft.NET\Framework\v2.0.50727。你可以使用aspnet_compiler -h来查看完整的帮助,例如编译一个IIS默认站点中的ASP.NET子站点可以使用这样的代码:
aspnet_compiler  -v / -p C:\inetpub\wwwroot\site C:\output\site

接下来,我们到输出目录的bin子目录里把dll抓到Reflector里面看看吧。你会看到这个dll里面有三个namespace,分别是-(在Reflector中代表没有namespace)、__ASP、ASP。假设你编译的站点有一个Default.aspx,那么在无namespace的类当中就会有一个_Default的类,对应的就是Default.aspx.cs编译出来的类。大家应该还记得《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》里面提到的,直接继承自Page的类是用后台代码编译出来的,_Default类就是这样一个具体例子了。我们打开_Default类来看看,就会发现MultiView1已经是其成员了,为什么呢?MultiView1仅仅在ASPX中声明,没有在C#中声明啊。回头看看《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》就能解释了这种现象——Default.aspx.cs是标记为partial的,而在你手动编辑的文件中,这是唯一一个partial,另外一个partial由编译器根据Default.aspx自动生成,编译器解释完Default.aspx后在自动生成的partial中定义了MultiView1,因此两个partial合并编译后,_Default类自然就有了MultiView1这个成员了。

接下来,我们再看看ASP这个namespace下的default_aspx类,这个是ASPX文件继承自上述_Default类后编译的结果,它完整表述了ASPX文件中整个控件树的逻辑,而不仅仅是一个包含一堆成员控件定义的Page派生类。这个类的执行入口是FrameworkInitialize()方法,它通过调用__BuildControlTree()方法来构建控件树。在这个方法里面,你可以看到<!DOCTYPE ...>这样的字符串是被解释为LiteralControl的,LiteralControl在Render()时就会把这段文本原样输出。同时你还可以看到,它调用了另外两个方法,分别用来构建HtmlHead和HtmlForm,这两个方法通过类似的形式继续调用其他方法来构建更深层次的控件。

通过阅读default_aspx类的代码,你已经能够理解ASPX的控件树是如何转化为C#代码的了——采用的正是Builder模式。了解到这一点,这次动手试验的目的也就达到了。如果你看到文章开头的那个问题时,你已经想到了使用Builder模式,那么此时也就验证了你的想法是完全正确的。

下一次的文章将是与TemplateControl相关的,我们将继续动手做一些小实验,敬请期待。欢迎订阅我的blog:

2008年3月14日星期五

在校学生找实习、找工作、了解企业情况等等等等

因为时不时就有低年级的同学跑来问我这类问题,所以我干脆写篇文章好了。

信息获取

最先想到的,也是最重要的,是你想干什么,而不是你父母想你去干什么,或者哪个赚钱之类的。在计算机系里面,你总能碰到一些对这个行业没什么感觉的人,他们会说当年填报志愿的时候根本没什么喜欢不喜欢可言,于是在父母驱使下或者金钱诱惑下就报了计算机系。显然你不想犯这类错误,因此第一步是弄清楚你想要什么,或者说,有什么是你可以选择不要的。世界上没有十全十美的事情,所以一切都是权衡取舍(trade off),这个思想贯穿着计算机体系设计的方方面面,缺乏这种思想肯定会导致你在这个行业里发展受到限制,所以首先你要想好的就是你将来的职业发展取什么而舍什么。

在你先清晰了解自己之后,才有如何了解企业实际情况的问题。如果说第一个问题严重依赖于你个人悟性的话,那么第二个问题就依赖于你的人脉了,至少是你的外向程度和活跃程度。

一个企业,其自身对外的公关肯定只会说好话,就算没有任何夸张成分,也不会让你看到这个企业内部任何的不足。因此,如果你需要知道一些细节,看看这企业是否如你想象中那么好,你就必须认识在这家企业里面的员工,通过他们好好了解一下。如果你是一个在学校里已经很活跃的学生,整天在BBS上板聊,或者参加各种学生社团的活动,那么你肯定有机会认识到不少比你高一两个年级的同学,看看他们里面有没有一些正好在你想去的企业工作的,联系他们了解一下企业的情况,例如工资和各种福利怎么算啊,工作与学习的氛围如何啊,以及他们是否也认为你适合这个企业。

如果有机会多问几个人的话,最好都问问,并且以人生经历和价值取向与你相近的人的说法为主要参考。这是因为,在同一家企业里面,不同的员工看到它不同的方面,并且都带有自己的主观想法,因此得出的评价可能截然不同。这就好像假如你问我北京是否好住,我肯定说不好住,因为我看重的是商业环境,我觉得北京的服务业经常让我失望,因此就这样说。但肯定也有人很喜欢住北京的,他看重其他一些因素,并且觉得我看重的那些因素可以忽略不计。如果你确实很想去一家企业,就应该多找几个员工了解情况,特别是头脑清晰能看清楚自己所在企业优劣势的人。如果你碰到一个已经被洗脑的员工,那么问什么也没用,反正他就只能帮你洗脑……

联系途径

根据往年的经验,现在正是2009年毕业的学生找实习的时候,甚至有2010年毕业的学生也提前开始找实习了。积极的人肯定早已开始四处打听消息,包括各大公司招聘什么实习职位,往年难度如何,转正比例多少,等等。在这里,我就要“曝光”我身边的一些朋友了,大家不要怪我“出卖”朋友了哦,因为谁都知道成功把有才华的人推荐给自己公司的意义所在。

在这里,我主要说的是BGM(Baidu、Google、Microsoft的意思,不是background music),如果你对其他IT企业有兴趣的话,也可以找我帮忙,只要我认识该企业的人,我会充当一下“路由”的角色为你“尽力服务”的。

Baidu

如果分别把BGM比作人的话,Baidu是一个典型的中国年轻人,一点都不张扬。就如同在中国随手抓一个ASP.NET MVP问他,“你是不是很熟悉页面生命周期”,他可能回答道,“懂一点吧,有什么问题你先说说看。”虽然从资源(resources)的级别来说,Baidu是很难和Google、Microsoft去比的,但是开放程度(openness)还是是相当高的,你可以做自己喜欢做的项目,可以获取到项目所需的资源。

想去Baidu的可以找我,或者布丁,先了解一下。当然,如果你来找我的话,最好你也能说服我为什么值得推荐你。我主要做Web前端开发的,而布丁则是做后端开发的,所以如果你想申请这两个方面的职位,可以直接找我们了解Baidu现在所采用的技术或者流程。如果是其他职位,也可以帮你联系其他同事问问。完整的实习职位列表,请看这里

Google

Google是一个典型的美国年轻人,你不问他也会很主动地把他的优势展示给你看。在我看来,Google最大的诱惑不在于公司内的“饮食娱乐”项目(虽然这些也很吸引),而是作为一般的中国员工也可以随便跑到美国总部去,顺道参加美国举行的一些会议或者讲座。看看Junyu同学就跑到Austin去开SXSW了。

如果想去Google的话,去找Junyu问问吧。别告诉我找不到他的联系方式,我通常只链接别人的blog,因为该URL就是个人的标识(即使抛开OpenID不谈),拿着这个URL你手上就已经掌握了搜索他联系方式的一切资料。Junyu喜欢把人拐卖进Google里,想了解招聘职位什么的,找他就好了。

Microsoft

Microsoft是个稳重的美国中年男人,Google所晒的东西,Microsoft会暗自想着“我当年又不是没晒过,现在我已经不屑于晒了”。很成熟的流程,健全的管理体制,都很好地说明了这一点。

貌似我没有很熟的朋友在Microsoft,不算太熟那些又不敢晒出来,所以想找人推荐的话还是联系我,然后我再帮你联系吧。想了解的话,可以找Jeffrey或者Dflying这两位老员工加现任MVP,他们现在不再是员工了,可能观点会更加客观一些。

其他事情

据我所知,很多人也是准备找实习了,才想到要写简历的。这是一项需要创意的工作,在此我提供我的简历Junyu的简历给大家作为参考。虽然我们的网站下方都写着Creative Commons License,不过不建议你直接使用别人设计好的模板。特别是,假如你想申请Web Developer/Designer类别的职位的话,你可不能够错过这个机会好好展示你的设计风格和编码艺术。

最后,祝大家在08年里都能找到自己喜欢的工作学习环境,好好享受生活。

2008年3月8日星期六

使用 .NET 实现 Ajax 长连接 (Part 2 - Mutex Wait & Signal)

在上一次的文章中,我们说到了如何设计一个ASP.NET Web Service来处理长连接请求。很多人对此就提出了问题,如何hold住请求让它30秒不断开了?这其实很简单,只需要Sleep()一下就可以了:

Thread.Sleep(30 * 1000);

然而问题是,我们不是要等30秒然后看看是否有事件需要返回,而是在这30秒内随时有事件随时返回。因此,我们需要一套机制来在等待的过程中检查是否有事件发生了。

Monitor模型

在.NET里面,大家最熟悉的线程同步模型应该就是Monitor模型了。没听说过?就是C#的那个lock关键字,实际上它编译出来就是一对Monitor.Enter()和Monitor.Exit()。

通过lock命令,我们可以针对一个对象创建一个临界区,代码执行到临界区入口时必须获取到该对象的锁才能执行下去,并且在临界区的出口释放该锁。然而这种模型不太适用于解决我们的问题,因为我们需要等待一个事件,如果使用lock来等待的话,那就是说要先在Web Service外部把对象锁上,然后等事件触发了就解锁,这时候Web Service才顺利进入临界区域。

事实上,要进行这类型的阻塞,还有一个更好的选择,那就是Mutex。

Mutex模型

Mutex,也就是mutual exclusive的缩写,“互斥”的意思。Mutex是如何运作的?这有点像是银行的排队叫号系统,所有等待服务的人都坐在大厅里等候(wait)被叫,当一个服务窗口空闲时它就会发出一个信号(signal)来通知下一位等候服务的人。总之,所有执行wait指令的线程都在等候,而每一个signal能够让一个线程结束等候继续执行。

在.NET里面,wait和signal这两个操作分别对应Mutex.WaitOne()和Mutex.ReleaseMutex()这两个方法。我们可以让Web Service的线程使用Mutex.WaitOne()进入等候状态,而在事件发生时使用Mutex.ReleaseMutex()来通知Web Service线程。因为必须在Mutex.ReleaseMutex()发生后Mutex.WaitOne()才可能继续执行下去,因此能够执行下去就证明必然有事件发生了并且调用了Mutex.ReleaseMutext(),这时候就可以放心地去读取事件消息了。

简单示例

在选定使用Mutex模型后,我们来编写一个简单的示例。首先,我们要在WebService派生类内定义一个Mutex,还有一个代表消息的字符串。

Mutex mutex = new Mutex();
string message;

然后,我们定义两个WebMethod。为了把问题简单化,我们选用上一篇文章中开头所说的两个函数签名,也就说只能在一个Web Service内自己发自己收,没有发送目标的概念,也没有超时的概念,还没有可靠性设计。同时,我们将Message类型替换为普通字符串,以便于我们测试。

我们先编写发送消息的函数:

public void Send(string message) {
  this.message = message;
  this.mutex.ReleaseMutex();
}

在这个发送函数里,首先我们把消息放进了类内全局的变量中,然后让全局的Mutex类释放一个signal。这时候,如果有线程在等待,它可以马上执行下去。如果此时没有线程在等待,那么下一个wait的线程执行到该阻塞的地方就能够不受阻塞继续执行下去。

现在我们来编写接收消息的函数:

public string Wait() {
  this.mutex.WaitOne();
  return this.message;
}

接收函数一开始就进入wait状态。在得到signal后,需要做的事情就是把全局的消息返回给客户端。

亲身体验

最后,我们可以通过ASP.NET Web Service本身支持的Web测试界面来测试一下我们的代码。我们开两个浏览器窗口,一个进入Send()调用,一个进入Wait()调用。然后我们按照如下方法来测试:

  1. 首先执行Send("Hello"),然后执行Wait()。这时候你可以马上看到"Hello"。
  2. 首先执行Wait(),让它等待返回,这时候执行Send("Hello")。随后你可以看到Wait()那段返回"Hello"了。
  3. 按如下顺序执行:Send("Hello");Wait();Send("World");Wait();
  4. 按如下顺序执行:Send("Hello");Send("World");Wait();Wait();
  5. 按如下顺序执行:Wait();Wait();Send("Hello");Send("World");
  6. 按如下顺序执行:Wait();Send("Hello");Wait();Send("World");

你会发现这样一些奇怪的结果:第3个测试返回的是"World"和"World"。第5个测试先返回"Hello"的并不一定是先执行的那个Wait()线程。后者在某些情况下不是什么问题,特别是长连接中一般之后一个Wait()线程在等待中,所以我们可以不管。而前者,则是因为没有消息队列所造成的,我们只有长度为1的消息窗口,所以只能缓存最后一个消息。这个问题我们将在下一篇文章中解决。

小结

在本文中,我们看到了不同的线程同步模型的差异。Monitor模型的lock本质上是一个Semaphore,也就是一个不能连续signal的Mutex,一个signal发出去后必须被一个wait接收了才能进行下一次的signal。同时,Semaphore也限制了signal和wait必须在同一个线程内成对执行,而Mutex则没有此限制。虽然.NET是针对Monitor模型优化的,但在我们的需求当中,只能通过Mutex模型来解决。

接着,我们便写了一个小小的消协发送与接收函数,实现了我们想要的阻塞式Web Service。同时我们也看到了没有消息队列造成的问题,因此确定接下来我们要做一个消息队列。如果你想知道消息队列如何编写,欢迎订阅我的blog:

2008年3月4日星期二

拿到了美国签证

准备了一大堆的材料,包括在校证明和百度的工资证明,结果签证官只看了我的邀请函,问了几个问题就搞定了,真爽快。他问的问题包括,我现在在什么学校(企业),去美国干什么,MVP是什么,大概就这样了,超级简单,还弄得我之前紧张了那么久。

最有趣的地方是,原来驻广州的是美国总领馆,而驻北京北京的只是大使馆,移民业务都在广州处理。整个广州大使馆都是说广东话的,除非看到你的签证签发地是非广东话地区,那就会说普通话或者英语。在移民大厅内,新移民在广东话的带读下进行宣誓,非常好玩。

2008年2月26日星期二

Imagine Cup 2008 - IT Challenge 晋级 Round 2

今年的Imagine Cup已经为没有Web Development了,估计因为往年Software Design太多人做Web了,所以干脆合并这两项了。今年新增项目为Game Development,用的工具是XNA Game Studio,看起来容易,但要做一个完整的游戏并不容易。总之,我们去年的团队今年没有再团队参赛,所以我自己挑了两个个人项目来玩玩。

第一个是Algorithm,因为我从来不知道Algorithm是怎么玩的,本年也仅仅参加了最后一场Round 1的比赛,所以在我知道怎么比赛时,已经在比赛了。我只能想尽办法拿分,没有任何经验和技巧而言,然后就挂了。

第二个是IT Challenge,同样是等到Round 1最后一场才参赛。这是考察MSIT技术知识的吗?还是考察Google的使用能力?不管了,反正30道选择题,我一看就懂做的大概只有两题,所以整个比赛就是靠Google。一个小时里面,每道题可以分配两分钟的查找时间,并行处理的话,打开页面的事件可以忽略不算。

比赛完,看我答对的题数,觉得自己不怎么样,结果竟然进入Round 2了。有趣的是,Round 2的资料一直没发出,甚至没有邮件通知,我是主动到ImagineCup.com看才知道自己进入Round 2了的。不过因为Round 2至今为止还没有发布任何资料,我也没办法做什么,只有等邮件通知了,或者天天上去刷新。

使用 .NET 实现 Ajax 长连接 (Part 1 - Comet Web Service)

Ajax的长连接,或者有些人所说的Comet,就是指以XMLHttpRequest的方式连接服务器,连接后服务器并非即时写入相应并返回。服务器会保持连接并等待一个需要通知客户端的事件,该事件发生后马上将数据写入响应,这时候客户端就以相当“实时”的方式接收到事件通知。具体的通信模型,请参考这篇文章:《Comet:基于 HTTP 长连接的“服务器推”技术》,里面已经说得非常详细了,我就不再复述了。

我们接着开始讨论如何使用.NET实现这个模型。首先我们能想到的是,我们需要一个Web Service,可以是ASP.NET Web Service,也可以是WCF Web Service,ASP.NET AJAX Library两者都支持。在这里,为了简单起见,就选择大家更熟悉的ASP.NET Web Service举例。然后,我们写下以下两个函数签名:

public void Send(Message message);
public Message Wait();

其中,Send函数用来发送一个Message对象,而Wait函数用来等待一个Message对象。然后,让我们来讨论一些细节问题。

无事件导致超时

首先,长期保持连接时不行的。对于服务器和客户端来说,这不是个问题,但我们永远都要记住中间可能存在各式各样配置怪异的网关和代理,它们上面可能有各式各样的超时规则,因此Comet最好设计为定期重连。一般情况下,如果30秒没有任何事件发生,服务器端就应该通知客户端确实没有事件发生,结束掉本次请求,然后重新开始一次新的请求以便继续等待。

那么上述函数签名可否用来返回一个无事件的消息呢?这是显然可以的,我们可以选择返回null表示无事件,或者返回一个EmptyMessage常量,这视乎我们使用class还是struct来定义Message。(甚至,我们还可以做一个名为NoMessageMessage的Message派生类来做这个事情。)

定义发送目标

上述函数签名确实能用来收发消息,但是没指名发给谁。可能有人会说,发送给谁可以在Message类里面通过一个属性来定义啊。但是Wait()方法没有说明接受方是谁,服务器端依然不知道哪些消息应该让你接收。

因此,我们引入Channel的概念,Channel使用其名称来标识,相同名称的就必然是同一个Channel。在发送与接受时,通过名称指定要发送到哪个Channel,这样问题就解决了。此时,函数签名修改如下:

public void Send(string channelName, Message message);
public Message Wait(string channelName);

可靠的消息队列

想象一个可能发生的情况,服务器端向你发送一个消息,你没有成功接收,但是服务器端认为发送了就成功了,消息从队列删除了,然后这个消息就永久丢失掉了。可能有人会强调TCP多么可靠,服务器端发送的消息如果在TCP的层面发生问题了,肯定会引发Socket级别的Exception,这个Exception冒泡上来,服务器端就能截获,从而得知发送失败,然后先不删除队首消息。可是别忘了,中间是可能存在代理的,如果代理成功把消息收回去了,可是代理发送到客户端这一步失败了,服务器端就不一定会发生异常了。

因此,我们需要制定一种策略,来确保下行消息总能发送到客户端。在这里,我们选择了引入逐个ACK的机制,来确认消息的接收。也就是说,服务器端发送给客户端的消息带有一个序号,在客户端收到消息后就将该序号发回给服务器端,已确认它受到了该消息。这时候,函数签名更改如下:

public int Send(string channelName, Message message);
public Message Wait(string channelName, int sequence);

我们使用Wait()接收到的Message中,应该有一个Sequence的属性,标记它的序号。然后,再我们执行下一次Wait()时就将该序号加1的值通过sequence参数传递回去,让服务器知道我们期望下一条消息的编号是这个。例如我们收到Message,其Sequence属性为836,那么下一次调用Wait()的时候就传给服务器837。服务器端此时应该保留了编号为836的Message在对首,如果客户端继续请求836号消息,证明它上次没收到,这次仍然发送836号消息给它;如果客户端请求837号消息,证明它成功收到836号消息的,这次就发送837号消息给它。

如果都不是,那该怎么办?那意味着,这是一个错误的请求,甚至可能是攻击请求,因为正常情况下不应该出现这样的请求的,服务器端可以考虑抛个无关紧要的Exception(不要告诉攻击者你知道他在攻击了),甚至直接给个400 (bad request)的响应代号。

与Wait()类似的,Send()也可以加入ACK机制,只需要将返回类型从void改为int就可以了,这个值就专门用于传递消息编号,实现方式和Wait()是一样的,不过Send()是由客户端保存待发送消息的队列。

小结

到此为止。我们的Web Service就写好了。这就写好了?只有签名没有函数体?是的,复杂的工作留给model去做,Web Service在这里只是相当于一个view,用于将model的接口暴露出来。

在下一次的文章中,我们将开始讨论如何实现服务器端的消息传递机制。如果你对本文章系列感兴趣的话,欢迎订阅我的blog:

2008年2月20日星期三

(Twitter++ == Blogger--)

不知道这个推论是否成立,Junyu曾经说过,“忙碌导致没时间把文字整理为blog,所以把思维碎片发不到Twitter”,现在我也差不多这个样子了。

首先,Twitter上的圈子不错,不用说我发了文章后,还要等各位订阅的来看,然后我写得有深度,大家也要想想如何有深度地回答;我写得没深度,大家想也不想就不留言了。Twitter上不用想,凭直觉,你觉得可以回的tweet可以直接回,重度Twitter用户都如此。在我的Twitter上,有Flypig这样的flooding狂人,有和他一唱一和的Junyu,最近我也开始和他们一起flooding别人了,还把awflasher也拉上来了。至于Yuancheng,他是饭否的人,我拉不动。

其次,如果一些思维的碎片,你在Twitter上发泄出来了,你就不想再整理成文了,因此也有人选择了自动把近期的tweet自动发布到blog的做法,例如hidecloud。不过我暂时还不想让tweet打乱我维护那么久的有深度的Blogger,所以还是不要这样做了。不过无论如何,发到Twitter,自然导致Blogger的减产。但是Twitter一旦变成重度用户就难以回头,所以这对我来说已经很难改变,除非好像Kevin Mitnick那样被判下半辈子不得接触互联网吧。

2008年2月19日星期二

数不尽的 blog 与 podcast

我发现未看的blog和podcast都太多了,已经到了无法处理的阶段了,该怎么办呢?最近大家都在自己的Twitter写到,“我终于把Google Reader给清了”,我不知道自己的什么时候能清掉,哈哈。

为什么如此之多英中字典对同一词条的解释与英英字典不同

随便找一个懂一些英语中国人,或者就说一个英语学习中规中矩的高中生,问问他affair是什么意思,他多半会告诉你“暧昧关系”,或者直接一点,说是“婚外情”。然而实际上,affair通常就用作一般的“事务”,例如“外交事务”之类的,在英英字典中“婚外情”的解释被排到很后的位置。

这里有一个问题,就是英中字典多数不是解释单词,而是翻译单词。通常,把英语单词放在一个常见的上下文中,然后整句翻译为中文,再把上下文剥离,这就得到了与该英文单词对应的中文短语,而这正是英中字典中所给出的结果。然而无论该上下文如何常见,这样得到的都不叫做解释,因为它没有解释原来英文单词的含义,只是把它翻译过来了。

由于大多数中国学生都习惯了这样的字典翻译,习惯了一个英文单词记忆一个对应的中文短语作为解释,从来不知道单词的真实含义,才造成了开头所说的问题。如果大家不要仅限于背一个解释,而真正理解单词的含义,那么问题也就解决了。

2008年2月10日星期日

需要针对平台编译的 Web App

很久之前,我提到过了CSS需要常量的问题。最简单的,定义若干调色板常量,整个CSS就用这些常量去标记颜色,以后要更换调色板也就容易了,只需要更改常量,无需更改逐个CSS属性更新。现在看来,可编程的CSS已经不足够了,在HTML + CSS + JavaScript中,我们都大量地存在信息冗余,而这些冗余的信息不符合DRY原则,手动维护的成本高,所以我们需要一种更高级的语言来自动生成这些冗余信息。

我现在使用Ruby on Rails,不过不是很熟悉。我有同事比较熟悉Ruby on Rails,非Rails的Web布局,他也有自己一套HTML + CSS模板,定制一下然后执行一段Ruby脚本,就build好一个output了。我觉得这是很好的一种做法,因为如果定制的工作是修改HTML与CSS的话,这就涉及大量的手工改动了,那就有可能改漏了或者改错了。

既然已经做到这一步了,不妨想象一下将来自动化操作是否能够更进一步,就是自动生成针对平台的HTML代码。我们用一种真正浏览器无关的语言编写行为与样式,然后服务器端自动根据user-agent来生成对应的JavaScript与CSS。这些JavaScript与CSS都是针对特定的user-agent生成的,所以不会有用于兼容浏览器的冗余信息。同时,因为它们是自动生成的,也就不需要人手维护这些冗余信息,生成它们的源代码也就能够保持DRY原则了。

2008年2月9日星期六

我的 iPhone 终于能当电话用啦!

花了一整晚的时间,从晚上9点开始,搞到凌晨5点,终于把iPhone软破了。

今年过年确实比较爽,Windows Server 2008发布了,然后iPhone原生1.1.2的纯软件破解方式也有人研究出来了。天天就在研究这些,过一个很geek的年,哈哈……

详细说一下破解的过程吧。我的iPhone买回来后,我自己懒得研究激活方法,所以让同事教我jailbreak,jailbreak之后就是1.1.1,我也懒得升级到1.1.2再jailbreak一次了,所以就当一个1.1.1的Touch先用了两个月。不过因为同事是用PC的,所以降级并进入恢复模式后,他不知道Mac上用什么软件把机器踢回正常模式,于是就在PC上用iBrickr完成了此操作。这次我亲自操作,开头在Mac上用jailbreak.jar总是不能成功,后来换到PC上用AppTappInstaller.exe才成功了。

我第一次升级1.1.2,按照iphone.unlock.no的教程,它没有写要装OktoPrep,我也就忘记装了,结果升级到1.1.2才发现不能jailbreak。这时候就降级再来。第二次升级到1.1.2,成功jailbreak,但是机器停留在恢复模式,我以为是jailbreak时没有关掉iTunes的缘故,于是再次降级……其实后来想一下,因为jailbreak.jar重来不能成过把我的iPhone从恢复模式踢出来,这次应该也一样吧。第三次升到1.1.2,jailbreak,然后换到PC把它从恢复模式踢出来,终于成功了,能用而且有信号!

做过一次后,才发现也不是那么难。开头想着破解后就在1.1.1用,懒得升级到1.1.2,结果发现破解后必须升级到1.1.2,否则没有信号,换上移动的SIM卡后还是相当于一只Touch。无奈之下升级1.1.2,结果一晚就做了三次升级,也终于知道升级怎么做。

2008年2月8日星期五

Windows Server 2008 发布了

过年之前就看到x64的版本出现在MSDN Subscription上面了,可惜我准备拿一台x86的机器来试试,所以就选择了继续等。今天终于看到了x86的版本了,现在看时下载,之后就装到服务器上看看效果如何。

我现在比较关注的是,它是否能在安装初始化时成功加载RAID驱动。因为Windows 2003最开始的版本是没有RAID驱动的,如果有RAID设备就必须在安装开始时把驱动软盘插进去,让安装程序从软盘加载驱动。这年头还有软盘?没错!而且是依赖于软盘。到SP出来之后,貌似是补上了RAID驱动,才省去了软盘这一步。如果Windows 2008又变成要软盘的,我就晕倒了……希望不是吧。

2008年2月7日星期四

原来 Vista 对 FAT32 的支持比 XP 要差

自从我开始使用MacBook,我的移动硬盘就从NTFS改为了FAT32。说实在的,对于FAT32这种这么老的格式,我实在没什么信心,但是在PC与Mac之间搬数据也没有第二个选择了。况且,FAT32还不支持4G以上的文件,要把一个DVD镜像搬到另外一台机器就必须分割。

之前我已经试过几次了,硬盘经过OSX写入数据后,在Windows就无法再识别出来。打开磁盘管理器一看,竟然格式成为RAW,连修复的机会也不给我了。这时候我能怎样做?只能在OSX中把几十G数据转存,然后把移动硬盘重新格式化一遍,然后再把数据传存回去。这时候,Windows中又能读了。

于是,我在网上搜索OSX和Windows经移动硬盘交换数据的方案,结果大家都说FAT32就行了,而且兼容得很好。我就很郁闷了,为什么就我遇到问题,从此我也尽量避免硬盘在两个系统之间转来转去,特别是避免从OSX写入数据,因为我觉得是OSX写入了一些数据,导致Windows不能识别该硬盘。

直到最近,我才发现问题的来源。我将移动硬盘从XP的机器上拔下来后插入到Vista的机器中,这次Vista又将它识别为RAW格式了。这时候我就明白了,不是OSX写入的数据有问题,而是Vista对FAT32的支持有问题,竟然XP写入后Vista也会读不出来。

这就真是没办法了,看来保留一个XP还是很有必要的,Vista的兼容性不错,但对一些老设备的支持还是有问题(FAT32算是老设备格式了吧),况且我也不知道对FAT32的支持是否能在SP1修正。

2008年2月6日星期三

Google Social Graph API 体验

Google推出了Social Graph API,允许大家更加便捷地搜索Google抓取到的XFNFOAF数据。

XFN是一种基于XHTML的Microformat,在<a />标签的rel属性中加上各种各样的值来指示目标URL(的拥有者)与本URL(的拥有者)的关系。例如me是指代自己,friend是指代朋友,met是指代见过面,等等。

FOAF则是基于RDF的一个扩展,原本RDF用于描述资源,FOAF则把侧重点放到了描述资源拥有者上来。

Google对Social Graph API的使用方法范例是这样的:假如你新加入到Twitter,没有任何好友给你follow,那么Twitter就可以考虑引入Social Graph API来为你寻找已有的好友。因为你在Twitter注册时输入了自己的URL,Twitter仅需要将该URL题交给Social Graph API查询就好了。或许你提供的URL正是你的blog,上面链接到你好友的blog了,并且也用XFN标记了,那么Social Graph API就能将这些好友的blog返回给Twitter,Twitter发现原来这正是另外一个用户的URL,那就是说该用户就是你blog上链接到的那位朋友了。

这到底是否真的那么有效呢?我马上用自己的Twitter地址测试了一下:

http://socialgraph.apis.google.com/lookup?q=twitter.com/catchen&pretty=true&fme=true&edi=true&edo=true

Social Graph API在我的Twitter页面上,找到了我设置的URL属性,知道那是指向我的另一个页面。那个页面就是我的claimID页了,上面当然不乏XFN标记了的链接,于是它又找到了我的一堆blog。可惜的是,新的Blogger模板中的链接widget无法假如XFN格式,除非你放弃widget改回用全手动编写的HTML,因此Social Graph API无法通过我的那堆blog找到到我的好友。

Update: Twitter把following加上XFN信息了,所以你的following和follower都会被Social Graph API认为是contact关系。不过这又引入了另一个问题,就是Social Graph API总是认为关系是自动双向的,但其实我的一部分follower我并不认识,我也不认为我和他们存在contact关系。

步出感觉安全的区域

这是The Game里面提到的,意思是人总会选择在一个自己觉得安全的区域内徘徊,而拒绝离开这个区域。举个例子,内向的人,或者说不擅长与女性打交道的男人,可能就觉得只有和同类打交道才是安全的,出了这个范围就不知道怎样打交道,关键在于怕被别人拒绝,或者更严重的是遭受羞辱。

事实上,不仅仅社交方面如此,很多方面都会由于这种现象的存在,而限制了个人发展。技术上的依赖,会导致个人的技术跟不上潮流。我是在ASP.NET 2.0发布后才算是真正开始使用ASP.NET的,所以没有什么ASP.NET 1.1的包袱,然而有不少人却因为习惯了ASP.NET 1.1而不太热衷于升级到ASP.NET 2.0来,因为他们已经熟悉了ASP.NET 1.1,那是个感觉相对安稳的环境。现在,ASP.NET MVC出来了,而我手头又没有多少ASP.NET项目要做,我也变得不急于迁移到ASP.NET MVC了,顶多看看别人写的文章,自己没时间动手去实践。

生活上,也差不多,习惯了和谁住,习惯了住在某个城市某个城区,也就觉得只有这样的环境才叫做安稳。而如果真的要获得更多的机会,必须敢于放弃这些让你感觉安全的区域,大胆地走出去,面对可能迎面而来的总总风险甚至是危机,之后才可能获得安全区域内得不到的收获。

2008年2月5日星期二

MacBook 突然充不进电了!

昨晚回到家,把充电器插到拖板上,打开拖板的开关,就开始用电脑了,后来才发现一直处于使用电池的状态。尝试插拔了充电器几次,也没有解决问题,MagSafe上面的灯是不亮的,电池的硬件指示等也没显示在充电,我怀疑充电器坏了,然后感到非常郁闷──怎么早不坏迟不坏,偏要过年前坏!过年啊,有保修的话也没有人帮你修,如果充电器不在保修范围内,想买或许也难买。上网查了一下充电器的价格,80USD,或是600HK$,吓死了,大陆的价格肯定还要贵。

因为已经很晚了,所以就郁闷地睡觉去了。第二天跟布丁抱怨这件事时,他说可以把电池拆下来再装回去试试,他的HP曾经因为电池冻到不能识别而出现类似情况。我就试着拆一下电池,发现果然能充了!原来在北方还有那么多的保养问题需要注意的,不知道MacBook拿到南极是否还能用……

2008年2月4日星期一

退订 blogbus 所有 feed

身边有部分高中同学,就是从高中开始写blog的那种,当时国内最大的BSP是blogbus,所以用的就是blogbus。然而blogbus最近“出了点问题”,也不是最近了,持续了很多个月了,我今天忍无可忍,决定无论是多么熟的朋友,只要是blogbus的,一律退订,在我得知blogbus修正此问题之前也不再订阅任何blogbus的blog。

首先说说这个问题给我造成的麻烦,我在FeedDemon或NetNewsWire中对一篇blogbus的文章标记为已读,然而第二天这篇文章又变成未读,每天我都要手动将所有blogbus的feed手动标记为全部已读。一开始我认识只是blogbus升级过程中造成的小问题,就好像之前Live Space也会时不时这样,所以手动标记一下就好了,结果这样的事情一直持续到今天。最终我确认了一下问题的来源,因为blogbus把文章输出到feed时要在最后追加“相关文章”,而且这些“相关文章”还不停变化,因此Newsgator只能把变更后的文章当作新文章来处理。

在其他的blog平台中,作者有时候也会修改文章,为什么不会导致Newsgator把这当作一篇新的文章?这关键之处就在于blogbus不对文章输出guid,因此变更后的文章无法确认它是不是由一篇已经抓取过的文章更新而来的,就只能当作新文章处理。如果输出了guid,Newsgator一看这个guid已经被抓去过,也就不管了。另外一些feed reader,处理方式可能略有不同,而将同guid不同内容的文章重新标记为未读或标记为已更新,但至少不会把这当作一篇全新的文章。

不标准的feed总是让人很头痛,虽然现在feed的使用越来越普遍了,但是还有很多feed提供者自己没有完全理解feed相关的标准就发布feed了,导致使用该信息源时需要加入各种兼容性设计。

2008年2月1日星期五

怪异的国内 iPhone App 市场

环视国内最大的iPhone社区weiphone,你会发现没有多少人讨论Web App,大多数人关注的都是破解以及客户端应用,然而实际上Apple官方就收录了一大堆第三方Web App,按分类整理好了,其中有些热门的非常好用,另外一些也不错。

在这样一个环境里,国内开发Web App没有任何优势可言,还不如去研究那个还不完整的API然后开发客户端应用呢。况且,Apple这个月就要发布客户端SDK了,API文档就能够得到补全,原本的客户端应用总能修改一下就变得符合官方指引。不过,听说只有官方私钥签名后的应用程序,才能够从iTunes下载安装,估计这还是少数,因此Installer将依然有用,老的应用开发模型也不会消亡。

回到Web App的话题上来,如果要在国内做一个Web App并获得成功,必须很注意iPhone用户的构成。现在有一些iPhone用户,还没有把iPhone破解掉,而把iPhone当作iPod Touch用,那么这些用户就只能当作iPod Touch用户算了。有什么区别?iPod Touch只能在wifi覆盖区域内享用网络服务,而iPhone则能够使用EDGE(或者说至少是GPRS吧),享受网络服务的连续性要更好,总计时长也多得多。开发一个Web App,就必须很注重可访问性,目标用户虽然有设备,可惜根本没有网络覆盖,访问不了,那有什么意义。

我现在在公司和在家里都有wifi覆盖,迟一些回广州肯定也要马上买一个无线路由回家以确保我的设备都能无线使用。然而,有多少人能够有这么好的网络覆盖条件呢,根据我从weiphone观察而来的,其实还有很多人是只有有线网络连接的,要安装软件到iPhone就只能通过iBrickr从数据线传。例如Jim,他就觉得要wifi安装的东西是很不方便的,因为学校的wifi环境不好,家里也没有无线路由。然而我正好相反,我没有装iBrickr,因为我用Mac,又懒得在Mac上装类似解决方案,所以我就严重依赖于wifi。

总之,因为Apple的门槛设置,以及破解的流行,导致了iPhone用户数量虽多,可是他们所处的环境异常复杂,一设计一个Web App成功的捕获不同环境中的用户是非常困难的。

2008年1月27日星期日

Geeky meets Pretty

因为我在北京的圈子里认识不少geek,同时我又想认识靓女,所以就采取了这样一种策略──估计那么多geek都想认识靓女,所以就搞一个party吧,各自把自己认识的靓女都带过来,那就可以大家都可以认识到更多靓女了。也因此,party被命名为Geeky meets Pretty。

这次的party星期四早上第一次讨论,然后就确定星期天晚上搞了,效率异常高。我其实是担心准备不充分的,不过快过年了,也就没办法拖了,既然星期天晚上没问题,那就星期天晚上吧。开头我们预计5位geeky至少各自带以为靓女来,有10个人,搞一个小规模的party,不过最后来了15个人,证明人数并不是一个需要担心的问题。

对于这次party,从geeky到pretty,按1到10打分的话,我会给2或者3。在讨论的话题上,其实不算geek,只有一点点IT而已,因为在场的男士都是IT圈子里的,说到PR女士就郁闷了──在说Public Relation吗?不是,是Page Rank。然而,整个party的娱乐性不算高,一群高智商的人聚会,娱乐项目自然是杀人。杀人游戏要求思维运转,不过娱乐性就一般般了。这次的party没有任何的酒精,也没有什么很刺激的游戏,这是我觉得娱乐性低的一些原因,就是现场很文明,一点都没有high的感觉。看起来更像是IT界的聚会,可惜大家又不讲IT,所以有时候也会冷场。

因此,如何搞好一次party,其实是非常有学问可言的,我的意思是对于我们这些geek来说,有些人天生是party animal,那是我们无法比的。就好像都是MVP,是否擅长交际是人人不同的。Bean在三亚时跟我说,你看哪个MVP是不怎么social的,你就知道他主要是做线上活动的了,因为做线下活动的MVP都是很活跃的人,而作为职业讲师就更加需要懂得如何调动听众的氛围了。暂时我自觉觉得能够条理清晰地讲好一场讲座,但是不能做到新东方讲师那样15分钟一个笑话,如果我要获取那样的技能,前期我必须主动的去做准备,例如每场讲座之前先精心挑选若干适合插播的笑话。讲着讲着就能自然插一个笑话,这种境界对我来说是难以直接通过不主动背笑话而获得的。

这看起来和我向来坚持的英语学习方法所不同。学英语我不觉得有必要刻意背单词,反正用的频率越高,说明那个单词对你也就越有用,你也就越容易记住它。然而问题是,英语你平时可以不停地用,如果你平时不怎么使用英语,然后考试机会就那么稀有,你就只能通过背来准备。对于演讲和party,我乐意不停地花时间去参与,然后潜移默化地把一些技巧记忆下来,然而这样的机会不存在啊,我不能每天都去演讲,我也不能每天都去party,就算我时间上能够负担得起,也没有这样多的机会。演讲没有听众可以找镜子,party呢?因为party的机会实在太有限了,所以我要组织一次party,我就必须提前做好准备。这次准备不充分,娱乐性不足,下次我就要做足功课再来。

最后,我要感谢awyuanchengyou xujunyu布丁等人,感谢大家参与到组织一个party的工作上来,帮忙选定了party的场所,预定了包间,拉拢了靓女,以及埋了单。

2008年1月24日星期四

Zhuantie 与 Jinghua - 中国互联网文化的重要元素

既然有人研究GuanxiGanqing,那么我就来研究Zhuantie与Jinghua。

首先来说说Zhuantie(转帖)。上次和王兴等人的讨论中,说到为什么百度贴吧只允许吧主上传视频的做法比大众型视频站更有效利用资源。这是跟中国的文化背景有关,中国人中乐于创作内容的或者乐于贡献内容的暂时还非常有限。曾经在Wikipedia上见到有人说,下一代人叫做P代,P指Participate,大家都参与到Wikipedia贡献内容了。然而在中国,还要多少年才能把一代人称之为P代这很难说,现在大多数人还是缺乏创意,缺少闲暇时间去创作,大多数人对着摄像头连装cool的创意也缺乏,高质量的原创视频当然就少。在这个前提下,一个YouTube类的网站就难以通过UGC发展起来了,因为根本就没有UGC,变成了纯粹的转载站,所有人都是将他自己觉得还的视频再发布过来,而非发布自己的内容。而这就是所谓的Zhuantie文化──在中国,由于UGC的缺乏,变成少数内容被不断的复制粘贴,导致整个中国互联网上的内容缺乏多样性。

而被Zhuantie的内容是什么呢?这就是所谓的Jinghua(精华)了。国外的论坛系统是没有所谓的“精华帖”这一设置的,只有sticky用于发布公告。为什么需要精华呢?因为国内论坛的水化问题太严重了,没有哪个帖子不跑题的,而国外论坛很明确规定off topic就一律砍,你有新话题一律开新thread,不要再原来的thread上追加。这样一来,国内的论坛就难得有一个帖子是真正专注于一个话题,能够有所深入探讨的,因此一旦能够出一个这样的帖子,就变成精华了。

2008年1月23日星期三

请不要在 taxi 上使用谷歌地图

谷歌地图(而非Google Maps)的数据源Mapabc非常不精确,已经试过两次了,在北京去一个陌生的地方,在的士上打开J2ME版的谷歌地图,搜索出一个地点来,然后指示司机开过去,而结果表明那是一个错误的地点。虽然北京的士司机很不靠谱(停在红等前拿本地图出来看的都有),但是谷歌地图更不靠谱。

有人说,百度的数据源Mapbar同样不精确,因为中国的一切地理数据都属于“国家机密”,因此需要经过扭曲后才能发布。什么叫做扭曲?这样说吧,用过PDA的人都知道如何校正触控笔的,因为PDA屏幕的显示与触感两个层都是线性的,一般三到五个点就能校正。然而中国公开到网上的地理数据,例如一个城市的地图,你把这张地图叠加到Google Earth的同一个城市之上,然后采用类似的方法来校正,你会发现极难对齐。理论上,如果两张地图都是正确的,只要比例和位置校正了,叠放在一起应该是每一个点都对齐的。我们暂且假设Google Earth拍出来的照片比例误差不会太大,那就说明Mapabc的数据源是有问题的。当然,上述都是听说会来的,我还没亲自做过Mapabc的地图校正。

这就是安全与便捷之间的trade off,你要增加一样就必须削减另外一样。如果你询问普通市民,他们肯定都优先选择增加便捷性的,因为安全问题暂时不觉得有多严重。这和企业内的信息安全问题一样,所有员工都会觉得企业的安全策略(例如每3个月改一次密码)是一种负担,从而想尽办法绕过去。

2008年1月22日星期二

大忽悠争霸战 Round 1 - DING!

aw在一篇关于学习英语的好处的文章里说到,有很多细小的题材只有英文文章才有所涉及,而在中文领域不一定有人研究过,甚至不一定有相关文章。而且,aw说中文文章经常有些是没有科学依据或者缺乏引用出处的,我将此理解为“忽悠”,那么我们现在就来看看谁更忽悠。

aw的后一篇文章的标题是:好消息 - IE6将退出历史舞台。世界上竟然有这样的好事,而我不知道?我一定要过去看看,然后我看了aw的文章,也看了他引用的中英文原文(英文来自InfoWorld,中文来自Solidot,Solidot到底和Slashdot是什么关系我并不清楚),结论就一个:要么aw被Solidot忽悠了,要么aw在忽悠别人,到底是哪个呢?

InfoWorld的标题是Microsoft warns businesses of impending autoupdate to IE7,意思是“Microsoft警告,商业用户即将自动升级到IE7”。然而,Solidot的标题却是“微软将在2月12日把浏览器强制升级到IE7”。这种翻译方法叫做什么?提示:不叫“翻译错误”。没错,那就叫做“忽悠”。“自动升级”就相当于“强制升级”吗?即使你不理解前面的“商业用户”是指什么,这两者的差别也显而易见。

接下来,我就InfoWorld的文章解释一下所谓的“商业用户”。所谓的商业用户,就是指文章中提到的WSUS用户,而什么是WSUS到Microsoft的官方网站看一下就知道。简单来说,你的企业中有一万个Windows客户端,它们都要做Windows Update,然而如果它们都从官方站点进行Windows Update,这浪费多少企业对外的流量,于是你可以选择使用WSUS──一个企业内部专用的Windows Update服务器平台。作为企业的IT管理人员,你首先在企业内部署一台WSUS,让它从Microsoft的官方网站自动下载Windows Update,同时用WSUS生成一个小小的安装包派发给每一个客户端安装一下。在所有的Windows客户端运行该安装包后,它们的注册表都会加上一个标记,告诉它们以后Windows Update不要从Microsoft官方网站下载,而从企业内部的WSUS获取。

这样做有什么好处?记住,在高度IT化的企业内,IT部门就是克隆人军队的指挥官,而所有客户端机器都是克隆人。举个例子,如果你企业内部的ERP平台依赖于IE6,在没有WSUS的情况下员工可能就会无意地通过Windows Update升级到IE7了,然后发现ERP平台用不了,这时IT部门就要被“打扰”了。这类情况一旦增多,“打扰”就升级为“骚扰”了。所以,作为指挥官的IT部门,有权选择“我知道IE7的存在,但不允许任何克隆人升级到IE7,它们也看不到IE7这个升级的存在”,然后所有“升级到IE7并与ERP平台不兼容”这一问题就不会发生了。如果员工自己下载一个IE7的安装包来安装呢?不要以为Microsoft和企业的IT部门是吃饭不干活的,Windows中复杂的组策略设置就是干这个的,IT部门可以定制一个组策略不允许员工装软件,或者不允许员工装IE7。记住,客户端机器就是克隆人,要么清一色IE6,要么清一色IE7,异类统统枪毙!

现在,大家理解什么是WSUS了吧,那也就应该理解那个“警告”的用意了──如果你是企业的IT管理人员,现在最好马上做调研以决定你的企业是否适合升级到IE7,如果不适合你需要提前设置WSUS以避免这个“自动升级”的过程。

当然,国内的企业,包括大型IT企业,其自身的IT化程度还不高,所以没有人理解WSUS以及类似的自动化流程,以为一切就应该是手动的操作,Microsoft的设计一切都是那么“蠢蛋”的,然而事实上谁才是真正的“蠢蛋”?我看是国内某些习惯手动操作并且不怎么理解“国外先进的科学管理流程”的译者吧。Solidot把InfoWorld和Slashdot都引用了,人家两篇文章都说明了这是Microsoft对企业IT管理人员提出的一个“警告”,它自己却在那里坚持说“强行升级”,这真是绝顶的忽悠,你看这带着6点PageRank的aw也中招了。

我在这里可以对Microsoft的客户划分再补充一下。MVP分为三个大类:第一类人的工作是,决定企业购买Windows服务器还是Linux服务器,研究哪个的使用成本更低;第二类人的工作是,在第一类人选择了Windows之后,判断企业内部管理平台到底是否基于Web,是否要引入RSS支持,以最低成本实现效率最大化;第三类人的工作是,在第二类人作出判断后,开始设计、编码、测试、维护那个可能支持RSS的Web平台。在中国,第三类人是最被人广泛了解的,第一类人如果挂个CXO的头衔也为人广泛了解了,而第二类人通常就被无视了。

2008年1月21日星期一

最 Geeky 的企业内部首页

首页嘛,有很多中方案,例如iGoogle式的,足够大众化。PageFlakes或者Netvibes也差不多。但是说到geeky,我有个不错的注意。

Opera提供一个Speed Dial,将1~9对应9个链接,而非将一大堆常用功能扔出来,这不错。根据2/8原则,就算我收藏的链接再多,就算我放在iGoogle首页的模块再多,我要用的就那么几个。因此,我设计的geeky首页就一个speed dial。就这样?当然不是,我希望首页不仅仅能做speed dial,还能做普通的dial。

好像普通的手机键盘那样,长按才是speed dail。例如长按4(ghi),那就打开Google;按4(ghi)6(mno)就打开GMail。然后,如果自定义首页只能打开URL,那就太无聊了。现在整天都要打开内部通讯录,查某个人的电话,然后拿起电话来拨号。我觉得我直接在Web界面上拨号就好了,然后自动转为Skype拨号。每人拿一个Skype手机……算!手机太大了,每人拿一个Skype耳机,假如wi-fi模块能够做到耳机里面的话。只要在首页输入电话,就自动转为Skype拨号,并且转接(或回拨)到我的Skype耳机上,那就够好了。反正公司内部wi-fi覆盖,走到哪里都一样,不会再出现打电话给某人发现他不在固话附近的情况。如果已经有了wi-fi覆盖这样的好事情,再搞一个固话网络或者3G接入,那就多余啦。

当然,有时候还是要查找某个人的电话号码,或者某个文档的信息,这时候不要输入数字,而先输入英文字幕(或者先输入数字0),然后就开始进行企业内部的全文检索。例如有人想找我的分机号码……还什么分机号码呢,我是说Skype号码,那就输入“Cat Chen”,然后就查到了,接着点一下就转到他的Skype耳机上拨号了。

2008年1月18日星期五

MacBook Air 如果可以这样子用就很 cool 了

大家都在谈论MacBook Air,我也来评论下这个东西。大家肯定对那个从大信封里拿出MacBook Air的情景印象深刻吧,我来提供一个理想的用例。

香港的黑帮电影中,你可以看到两方boss会面时,各方boss都会没收所有跟班的手机,然后装到一个大信封里,并且从另外一个大信封倒一堆手机出来,让大家个人拿一个,以确保没有人泄密。

如果MacBook Air足够便宜,boss会面前跟大家说,谁还没有电脑的,然后叫秘书搬一堆大信封进来,一人扔一个(“扔”这个动作很重要,这样才够cool),然后大家打开后发现里面装的是一个MacBook Air,那就爽了!

2008年1月15日星期二

原来我住的是百万豪宅!

其实我从来不在blog里写这样的话题,不过这个比较讽刺,所以说两句。我现在和另外3个在同样是在百度的广东人合租一个房子,房龄和我差不多大,不过竟然买卖时的交易金额能够飙升之百万,真是神奇。我们现在¥3400/月把它租下来了,淡季,离奥运还有半年,所以租金算是便宜,却最近才发现原来我们住的是如此值钱的房子,真是神奇!

出名有两种途径

首先,这是Benny在若干年前说的,不是我说的。想要出名,有两种途径,要么互相吹捧,要么互相攻击。没错,他这说法就把所有可能性都囊括再内了,事实上无论你是主动想成名,还是凭借实力成名后想保持身份与地位,这两样都是你的选择,也是你近有的选择。

我们可以看得到,有些业内“知名人士”,时不时发表一些其实没什么深度的文章,里面其实都是对业内其他名人的……这么说吧,就是他表现得在其他名人面前尤为谦虚的样子,这其实就是第一种途径了。你想想,如果你捧一下别人,那个人可能不感恩也来捧一下你吗?这当然可能,因为对方真的可能就是实事求是的人。然而你捧一百个呢?里面总有几个和你一样处于某种尴尬地位的,或者说是瓶颈,论实力想再提升上去有难度,于是干脆就顺你意和你一起玩这个互相吹捧的游戏,反正他也有好处。

另外一种途径,就是找个名人下手,开始攻击他,例如找Keso啦。先不说Keso到底是否有实力,这不是重点,重点在于他出名,那就够了,你就可以用他来做攻击目标,从而推动你的名气上涨。如果你只是一群暴徒中相对“默默无闻”的一个,那没什么作用,但如果你是领头人,你攻击别人的言论足够“一针见血”,那就成功了。正所谓“敌人的敌人也就是朋友”,那位名人的反对者就会站到你这一边来。也就是说,你只要不至于蠢到找一个人缘超好的名人来做攻击对象,找个性格分明的,他肯定有自己的反对派,你一攻击,就相当于把他的反对派拉到你这边来了,多好。

因此,国内的IT圈子有时候也挺像是娱乐圈,谁玩谁还不知道呢。我作为一个MVP,有时候也不得不随大流结识一下圈子里的其他人,例如去认识一下Keso。不过说到底,最值得仰慕的,还是那些真正靠实力而成为geek的人。

2008年1月10日星期四

Windows 就是一个带 UI 的命令行

这是用过Mac之后对Windows的感觉。

用一个对程序员来说比较友善的比喻来说,以面向过程的方式来写一大堆函数,然后再分别塞到几个类里面,这就是Windows了。而Mac是真正的面向对象思想下设计的。

在Windows当中,我们貌似在操作一个“文档”、一段“文本”、一张“图片”,但实际上我们都不是在操作对象本身,而是类似于在操作handle——你不知道对象在哪里,你不能直接传递或操作对象,你只能借助仅有的API对handle进行操作,所有能对handle进行的操作都由API限制了。例如即使你在操作的是一张“图片”,你也很清楚那是一个存放为文件的图片,或者暂存在剪贴板的图片,总之你必须清楚handle在哪,否则你就算看到了那张图片也无法操作——图片在网站上,你先另存或者复制到剪贴板吧。

这种体验不就完全是面向过程的映射?每一个操作相当于调用一个或几个API。Windows发展了那么久,在人机交互方面还是脱离不了对DOS的简单封装。在这方面,Windows从前落后于Mac,现在还是这样。Mac虽然不能达到最理想的面向对象操作方式,但至少系统本身提供的体验要比Windows好,同时主流软件也比Windows的主流软件在体验上要好一个等次。

例如Skitch这个轻量级的图片处理软件,你可以抓起正在编辑的图片扔到桌面上,这就自动另存为一个图片文件了。文件与内存中的图片数据对用户来说已经无缝连接起来了。

再举一个例子,在Windows我们都知道回收站是个很好的比喻,不用的文件当然扔那里面。不过说真的,你多少次把文件扔里面了?你用得更多的是delete按钮,对吧?这是显然的,因为Windows用户习惯最大化窗口,这当然就把回收站遮住了,傻瓜才会选择在窗口里拖动文件的同时回到桌面接着再把文件扔到回收站里。Mac的回收站叫做废纸瘘,它出现在Dock上,Dock类似于Windows的任务栏,永远不会消失,就算自动隐藏也可以在鼠标靠近后自动跳出来,因此你永远可以把你看到的不想要的东西拖到废纸瘘上面去。

这也说明了一个问题,Microsoft这样去仿造Apple发明的交互方式是没用的,因为一点点的差异就可能导致可用性完全不同。Windows Mobile 7泄露的文档中,透露了Windows Mobile 7将可能有一些类似iPhone + Wiimote的功能,例如用手指拨动CD封面从而跳到下一张或上一张CD,甚至晃动手机从而跳到随机抽选的一张CD,这样的交互到底是否好用,真的很难说。

最后,无论你在开发Windows应用还是Web应用,我都建议你去弄个Mac来用一段时间吧,不要给一直以来Windows及Windows主流应用程序的交互设计方式禁锢了你的思路,你应该看一点新鲜点的东西。在以前,我认为Scriptaculous的拖放库完全是多余的,真该好像Atlas转变为ASP.NET AJAX时那样把拖放库丢一边,但在使用Mac之后,我才真正觉得在Web应用中推广拖放操作是非常有意义的。

假如我们都做一样的事情

之前写过关于Facebook News Feed的文章,现在开始反过头来思考trend的坏处了,那就是我们都变成克隆人了。

其实所有寻找同好的网站都有这样的问题,一开始你能够很好地寻找自己的同好,以及他们的爱好,在相邻的领域内拓展自己的爱好。然而实际上我们都明白这在数学上的意义,就是所谓的“类聚”,欧式距离相近的若干个点就聚唯一个类,并且一个点与这个类中各点距离的总和比与另外任何一个类中各点距离总和要小。

之后呢?因为你在变得越来越像你的同类,同时他们也变得越来越像你,这样一个类内的多样性就开始缩减,这绝对不是一件好事。有两种途径可以打破类聚,要么是创造类聚本身的网站致力于重新打破类聚,这听起来很好笑对吗?你的服务目的是创造类聚,但你又要打破它。然而另外一种途径就是,由外力来打破这种类聚,之后你的服务也就真正失去存在意义了,哪一种才是你所期待的?

我们都希望同类的人不停地聚集到自己的平台上来,但这是不可能的,在完全朝着目标的发展路线上一种可控的“叛离”是必须的。你希望聚拢一种用户,但无论是你喜欢还是不喜欢,中间总会带来一些变化,例如新的用户群体超过了原来的用户群体,最终超过临界成为了所谓的“变味”。需要一再强调的是,这种改变是不可避免的,你不能说控制你的平台不发展从而不改变用户群体,他们继续类聚下去也会爆发,因此我们需要在“可控”的前提下进行一些操作。

无论是之前的论坛“火了-水了-完了”,还是麦田现在所说的“有趣-无聊-暴力”,这都表明群体的进化是必然的,我们需要研究的不是如何保持一个群体以避免其发展。虽然上面两个例子看起来都是“衰亡”——总之就是越变越坏,但实际上这是一种发展。我们需要的是控制这种发展,而不是任其自然。

2008年1月8日星期二

跑去见Keso了

北京给我的印象很一般,不过没办法,中国的IT人都聚在这里了,甚至IT以外的人脉枢纽也在这里。与之对比,硅谷不仅仅有人,气候貌似是我喜欢的,生活条件也不错,而北京在“天时地利人和”这3个条件中就只有最后一个,却吸引那么多人跑来这里了,说明其人脉的优势是如此之强大以克服前两者的弱势。

今天晚上在布丁的带领下跑去见Keso等人,第一个印象是这些人特别能吹水,第二个印象是Keso对香烟的消耗率实在高。这样一群人,能够从互联网隐私问题扯到视频管理再到色戒,中间还间插着Google Camp与燕窝这样的东西,实在强。而且那种思维的跳跃性,让听众的大脑很快就会觉得超负载。

在奥校我已经习惯了和一群思维超级跳跃的人聊天,这没什么问题,不过那时候我们都是搞理科竞赛的,我们习惯以geek的思维来扩展,我们对用词与用意是否低俗并不介意。Keso这些人,art背景够强,不像我们只有science背景,于是说话总是喜欢挑些一针见血的,同时又保持言语的高雅性,这样非tech类话题我就很难插嘴了。

总体而言,去和Keso聊天还是很爽的一件事。下星期看看布丁又能带我去见些什么人。周围去找人聊天是一件非常有趣的事情,能认识到不同的人,了解他们的观点,因为这些人的背景都非常不同,所以视点可以是非常独特的。

2008年1月7日星期一

编写 iPhone Friendly 的 Web 应用程序 (Part 6 - iUI)

iUI是一个针对iPhone Safari的Web开发框架,由Joe Hewitt开发。这位Joe Hewitt曾经参与过Firefox的开发,包括其中的DOM Inspector以及Firebug,后来到了Facebook,为Facebook开发了iPhone专用版本,对iPhone Web开发社区算是作出了巨大贡献。

样式

我们继续说iUI这个框架。这个框架所做的事情,就是提供iPhone Friendly的交互方式与样式。为了简单起见,我们先来说说样式方面,iUI提供的不仅仅是一个CSS文件,基于这个CSS文件你所创建的页面能够符合iPhone的人机界面指引,并且看起来的效果贴近iPhone原生的应用程序,从而降低用户学习门槛,避免你自己设计的Web应用对iPhone操作不友善(例如按钮不够大,手指难以点击)。

交互

至于交互方面,iUI提供一个基于page的换页导航机制。这里所说的page不是一个Web页面,而是一个<body />内的顶级DOM元素,每一个这样的DOM元素都可以作为一个page,同一时间上仅显示一个page。页面上的所有链接,要么导致page转跳,要么导致整个页面转跳。这种转跳有如下几种情况。

转跳

如果链接的href指向的是一个锚点,例如#somePage,那么iUI就会在页面中寻找id="somePage"的page对象,然后进行转跳。

如果链接的href指向的是站内地址,例如/somePage.html,那么iUI就会使用AJAX的方法加载/somePage.html的内容并追加到<body />内,注意,/somePage.html必然是一个文档片段而不可能是完整的XHTML文档,否则把<html />追加到<body />下就是错误的了。追加的内容内可以有多个page,如果其中一个有selected="true"的属性,那么接下来将显示该page,否则显示追加内容中的第一个page。

最后一种情况是指向站内地址,但是有target="_replace"属性。iUI在看到target="_replace"属性后,就会知道该<a />所在的page内直属元素要被删除,并且替换为目标页面的内容。在这里用CSS来解释一下所谓的直属元素,body > ul#somePage > li > a[target="_replace"],这里<li />就是<ul id="somePage />的直属元素了。这种转跳通常用于曾亮加载,例如iPhone内置的Mail打开邮箱后自动加载50封邮件,点击more之后再加载50封,Web界面上就可以通过这种方式实现——一个<ul />内包含51个<li />,前面50个对应50封邮件,最后一个<li />包含<a target="_replace" />的more链接,目标页面就是下50封邮件的<li />

在转跳的时候,iUI提供了一种很好看的效果,你能够看到当前page从屏幕左边移出去,新的page从屏幕右边移进来,好像PowerPoint的某种幻灯片切换动画那样。如果添加了axis="y"属性,page还能够子底向下滚动。

历史记录

AJAX式的页内内容更新不是不好,问题就在于浏览器无法自动保存历史记录,导致前进后退按钮实效。iUI已经解决了这个问题,上述3种转跳中的前两种iUI都会自动创建新的hash以便创建历史记录,hash默认就是#_pageId的形式,如果当前显示的page没有id,那么hash就按照增量自动分配id。

在拥有历史记录之后,用户就可以通过浏览器的后退按钮向前翻页了,此时相当于加载前面的page,iUI知道这是一个后退操作,就会提供反向的滚动效果(自左向右或自上向下)。

如果我访问页面的顺序是A > B > C > D > B,那么历史记录会如何呢?历史记录会变成A > C > D > B,也就是说第一次访问B时B从历史记录中删除并重新添加到最前的位置了。

演示

说了那么多,我们来看看iUI的实战效果吧!又是Facebook或者Digg的iPhone版?不是,我们来看看自己利用iUI开发一个小应用的效果以及成本如何。就在上个周末,我花了不到20小时做了个名为iBaidu的小东西,自动抓取Baidu的搜索结果与排行榜并以iPhone friendly的方式显示出来。以下是发布到YouTube的演示视频:

服务器端使用的技术是Ruby on Rails以及ASP.NET。为什么需要ASP.NET?这是一个很郁闷的问题,因为Baidu是基于GBK的,而RoR的编码转换能力其若无比,我懒得花时间去寻找适合的转码库,因此直接拿了ASP.NET来做抓去代理,抓取的时候顺便做一下编码转换。如果不是编码问题,那么只需要RoR就能轻松完成任务。

小结

在这篇文章中,我们已经看到了如果使用RoR搭配Prototype,不好意思,iUI才对,要设计一个iPhone friendly的Web应用程序是多么地容易,开发过程也相当敏捷。如果你喜欢iPhone开发系列的文章,欢迎订阅:

2008年1月5日星期六

什么时候抛弃Live Messenger

最近全面通知QQ上的所有好友,包括群,说明我决定于2008年开始停用QQ或TM,需要继续保持联系的话就加我的Live Messenger或GTalk,无论什么事情,至少可以通过GMail联系我。我期望的是,如果你觉得我们之间的connection不那么重要,就不要来烦我了,我不关心你上线下线这样的琐碎事情,记录下我的GMail吧,没事不要在IM上找我,然而结果却是另一种情况。

在我在QQ上广播暂停使用QQ且给出Live Messenger与GTalk帐号后,一堆人来加我的Live Messenger。因为我在群里面广播了,有些人本来不是好友的,还变成了Live Messenger好友。可能我们在群上根本就没有直接对话过,这样就加进来真不知道说什么好了……我在Live Messenger上也不知道如何给他分类。可能有些人认为,只要有价值的人,也就先加了,避免日后要联系又找不到,然而其实他过去没有任何联系我的理由,将来可能也一直没有。

总之,我现在Live Messenger上面的好友数量暴涨,并且我也从Live Messenger换到Adium上来了,看不到以前的聊天记录了,因此如果我不记得你是谁了,那么请见谅。可能经过一段时间后,我又会选择停用Live Messenger,以过滤掉一批不用联系的人。

2008年1月3日星期四

想买个数位板

最近几天就在fancy这个东西,不知道什么时候才有米去买一个。感觉用笔来画画很爽,况且平时也能用笔操作,鼠标就省掉了,不会因为长期抓鼠标导致健康问题。

2007年12月31日星期一

新年新服务: MVP 播客上线

其实我很早就想做podcast(播客),10月份从三亚回来一直在筹备一个MVP博客,最终在前天完成了第0集(pilot episode)的录制,并将于今天稍晚时候上线。在整个过程中,我需要感谢dudu以及博客园对podcast提供的支持与服务,感谢BeanJeffrey对podcast准备工作与录制的投入。

首先,我介绍一下podcast为什么叫做podcast。过往,我们能够订阅feed,从而好像收取email那样收取网上的信息。例如,我订阅了ABC World New,设置了我的电脑每天早上7:00自动开机并打开feed reader下载feed,那么当我准备好早餐后,我就可以一边享受早餐,一边阅读新闻了,这和传统的喝早茶看早报没什么区别。

然而,ABC World New的新文章,附带了一个新闻视频片段,以龟速的下载速度,我可不能等下载完看完再去上班,因此我希望我的feed reader能够识别视频音频片段并且自动下载。RSS 2.0的<enclosure />标签是第一个出来把问题解决掉的,只要把这一个feed entry相关的文件放到<enclosure />链接内,feed reader就应该懂得自动去下载。之后,我把电脑自动开机时间改为早上5:00,预留两个小时,什么龟速也能把几分钟的视频片段拉下来吧。然后,我还是边喝着早茶(或者咖啡),边看新闻,不过这次不用读文字了,视频里面有人说给我听。

随后,iPod和iTunes又把这个应用提升到了一个全新的层次。如果你使用iTunes订阅新闻,起床后我只需要按一下同步按钮,视频音频都传到iPod上面去了,然后专心吃我的早餐,出门口时记得把iPod从同步底座上拔下来带走就是了。我在地铁或公交上有足够多的时间,把iPod拿出来看看或者听听头条新闻的时间是绝对是有的,甚至还能听听有声电子书。

由于这个功能在iTunes与iPod上就被命名为podcast,而且iPod在美国相当普及,所以大家就都叫podcast了。不过,Microsoft貌似避忌所有pod字开头的东西,以保持和Apple之间的距离,所以就坚持把同样的东西叫做netcast,这样显得中立一些。不过Zune2里面的功能菜单就写着podcast,看来负责整理Zune2菜单字符串资源的人有被炒鱿鱼的危险了……另外,国内有一些名为播客的东西其实不是播客,土豆一开始就给人如此批评了——不支持RSS订阅,也不允许下载,更别说放到iPod上了,这也想带上播客的头衔?

好吧,对podcast的介绍到此结束,稍后MVP博客的第0集将会发布到netcast.cnblogs.com,并且我还会另外发贴详细说明如何订阅podcast。敬请期待!

2007年12月30日星期日

我嘅 2007

(以下的是实验性文章,全文使用Cantonese书写而成,附带Mandarin翻译,请将鼠标移至带有下划线的短语上以查看翻译。)

點解成個博客園都喺度“我2007”,仲要大部分都差唔多……好,我就寫唔同,例如……人脈啦。我要,技術唔係最重要唔係管理,而係人脈。

2007年,個人發展上兩個重要事件,一攞咗微軟MVP,二B家,兩樣都要靠人脈(要多謝人自然非常多)。,第一你要認識人際網絡中充當關鍵節點個人,第二個人要睇得起你。簡單,如果你有能力,無論係技術定係管理,首先你要識得渠道(或者叫中間商),其次要覺得你超值,先有機會賣得出去,而且賣個好價錢。渠道前提下,大家都明白廠家直接向客戶銷售成本有幾高,你一個人能夠聯係買家數量就非常有限

如果一開始,我唔係Dflying同埋Jeffreyblog上評論,佢哋唔會blog,亦就唔會佢哋鼓勵我將文章發到首頁,咁嘅話,可能dudu亦就唔會知道我存在,dudu推薦我去MVP就知從何談起。所以,MVP呢件事一定要多謝Dflying、Jeffrey同埋dudu。當然,仲要多謝所有讀者,特別有留言嗰啲,因爲有留言自然激發思考。

然而,MVP並唔係終點,只係另一個起點。之後,有中國MVP峰會,Bean介紹我唔少微軟同埋其他MVP,先有后嚟一啲合作項目,例如netcast啦。所以,認人有得做,因爲好多係呢度,我要多謝Bean、Jeffrey、dudu,特別dudu,完全講求回報支持我哋做netcast。

另外一件事,就關於賣身㗎嘞,我意思搵工。寫“我2007”入邊多人寫自己今年畢業搵工如何如何,我又可以寫啊。搵實習事就,全職申咗好幾間有噃咁上吓大嘅IT公司,包括BGM,其中B家G家過人做推薦(多謝布丁同埋Junyu)。B家最早offer,所以就賣B家算嘞G家M家申請算“無疾而終”(拒信,hold咗喺唔知邊個環節)。但係,我想唔係點樣賣畀大公司而係推薦有重要

如果人推薦,你簡歷可能石沉大海都話畀你知,而且石沉大海概率仲唔細添但係,有人推薦嘅話,你就清楚流程,其中每個環節對你評價如何都透明一啲你就可以不斷調整自己。推薦人熟悉公司内各個職位差別嘅話,對你選擇職位亦有幫助,好似高考選專業蠱蠱吓唔好啦。(如果你高考嗰時已經識得師兄問清楚專業你可以跳過。)

當然,最重要嘅係推薦你個人真係認爲你值得擁有呢個職位,而唔係應付你。積極幫你,就反映一點,影響推薦效力。唔好以爲幫你ERP或者HR之類系統填落力幫你件事,幫你同你面試官溝通你情況,你成數就越大。如果你搵到個人只係礙于人情而推薦你,根本覺得你將來成爲同事嘅話公司都有好處,你得到好處無非就係免筆試,對促進你公司之間互相了解一啲作用都

免筆試……免筆試又有用呢?其實筆試過後離offer仲有十萬八千里遠。聼講G家美國本來就筆試,只係中國應屆畢業生實在太多就筆一筆,篩一篩。有時甚至筆嘞,咁樣推薦推薦都係一面起步,你希望你優勢更好傳達面試官嘅話除咗面試時候表現要好,就靠推薦人溝通

最後,通過一個故事結束本文。曾經,Mary總係好有激情同人同埋新公司,其中一個聽衆就係Unity Way董事會成員:John Opel。五年之後,John Opel,亦就係IBM董事長,要為IBM機器一個操作系統。邊度?去WashingtonRedmond會見Mary即係Bill Gates。多謝同埋IBM單生意,Bill Gates做首富。如果John Opel從嚟聽講嘅話,IBM絕對唔會派人去Redmond一間當時幾乎Microsoft一樣默默無聞公司。

點樣放電

將件衫,特別係羽絨,剝落嚟,然後隨便搵嚿金屬物掂吓,咁就會放電嘞!喺北京D咁乾燥嘅地方,隨時都有可能無啦啦畀電親,真係抵死。不過我今日要講嘅放電唔係呢种放電……我要講嘅係attractive呢种放電。

要放電,首先要有電壓,亦就係一D你有佢冇嘅嘢。例如話,錢啦。又或者係一D更加虛無嘅嘢,所謂嘅demonstrate higher value就係無中生有搞D咁嘅嘢出嚟嘞。例如,你只係一般醒,但係扮到鬼咁叻鬼咁競,又搞到人哋信以爲真喔,你就搞咗個高電壓出嚟啦,如果人哋又過電,咁就掂晒嘞。

其中最重要嘅就係佢要信,咁先過電,於是你放出嚟嘅嘢就一定要係佢理解能力之内嘅嘢,而且最好係佢明知冇但又無能爲力嘅嘢。最好就係佢好希望自己能夠擁有嘅,咁既然佢得唔到,就當然希望從你身上得到啦。事實就係咁簡單。

2007年12月28日星期五

编写 iPhone Friendly 的 Web 应用程序 (Part 5 - 交互入门)

我们已经研究过XHTML和CSS了,现在开始看看最后一部分,也就是JavaScript,以及它所提供的交互能力。

无AJAX交互

第一种我们要看的交互,是完全不使用JavaScript,这其中一个例子就是GMail。GMail的iPhone版其实就是由普通的GMail移动版修改过来的,界面上更贴近桌面版GMail了,然而交互性并没有怎么提高,每一个点击都对应一次刷新,没有任何AJAX可言。

事实上,不用任何AJAX效果并不会让你的iPhone Web App低人一等,如果有人讥笑你的应用没有引入任何AJAX功能,你可以直接跟他说“GMail也没有”。因此,如果你在开发的过程中决定不把任何时间投入到AJAX相关技术的研究,这是没问题的,确保你的服务器响应速度,并且交互设计得当,那就行了。

以服务器端为中心的AJAX

接下来我们看看另外一些应用,例如之前说到的几个,就拿最简单的AppMarks来说说吧。首先,使用User Agent Switch更改你的Firefox的user-agent属性,伪装为iPhone,然后打开AppMarks,并且打开Firebug。接着点Menu -> Add -> Browse,看到出现AJAX请求了吧?猜猜这个请求是什么类型的,面向内容(传输更新上去的XHTML)、面向脚本(传输进行更新操作的JavaScript)还是面向数据(传输更新相关的JSON)?答案是——面向内容的!

这可是Web App哦,不是一般带有一点点AJAX的网页哦,我们要MVC,我们要“先进”的面向数据,为什么要“落后”的面向内容呢?至今为止,我们能够看到的大部分iPhone Web Apps,都将MVC保留在服务器端了,客户端唯一需要知道的就是内容更新,不存在任何的客户端MVC模型。暂时我还没看到有面向脚本或者面向数据的,如果你见到有应用这样做了,请告诉我。

我们继续说MVC的事情。现在大多应用的设计方式是这样的:每一个应拥有一个view framework,然后每一次点击所作的操作就是切换view。例如刚才说到的AppMarks,从Menu进入Add是一个view切换,不过因为Add这个view的内容是固定的,因此一早就包含在页面里,不用AJAX请求直接加载就行了。另外一种情况,就是好像加载Browse那样,是需要AJAX把view请求过来才能加载的。除了切换view,我们还需要action,例如提交数据就一定需要action的。说到这里,感觉是不是和RoR或者类似框架扯上关系了?事实上,RoR,或者类似框架,确实很适合用来做iPhone Web Apps。

以客户端为中心的AJAX

那么除了RoR,我们还有别的选择吗?我们可以选择使用一些客户端框架来实现类似的效果。这样说吧,类似RoR那样每一个view都是一个模板,但不是rhtml,而是普通的html,没有复杂逻辑,点击连接后不是由RoR引擎调在服务器端用rhtml,而是客户端自己直接拦截了链接点击并用AJAX的方法去请求该html然后更新内容。这样一个框架,可以在Wrox的Professional iPhone and iPod touch Programming : Building Applications for Mobile Safari一书中见到。这本书写着2008年1月出版,但实际上已经出版,并且可以下载源代码。我暂时还没办法买到这本书,但源代码中就包括了这样一个客户端框架。

然而,这种以客户端为中心的做法并没有真正在客户端引入一个MVC,它只是简单地把服务器端的MVC裁减掉了,服务器端只能简单的发送内容或响应提交,因此只适用于比上面的以服务器端为中心的模型更简单的情景。如果你正在编写的应用不涉及大量的提交操作,或者根本就是Web1.0网站,我的意思是,单向传递信息不接受任何用户提交的网站,那么这种轻量级的模型就非常适用了。

小结

在这次的文章里,我们介绍了三种常见的iPhone Web Apps交互方案,没有哪一个是绝对更好或者更坏的,按照你当前开发的应用做出选择吧。将来我们写文章深入探讨其中的一些实现细节,或者是交互模式,但前提是我先完成了几个iPhone Web Apps。欢迎订阅本系列文章:

2007年12月26日星期三

编写 iPhone Friendly 的 Web 应用程序 (Part 4 - CSS)

说到编写CSS,大家的第一反应肯定是——有没有选择性CSS。有!我们可以设计一个CSS,使得只有iPhone上的Safari会采用它,其他浏览器都会无视它,这样我们就可能可以复用现有的XHTML页面代码,仅仅为它们引入新的CSS就能够适用于iPhone,无须重新编写页面。这个选择性CSS链接语句如下:

<link media="only screen and (max-device-width: 480px)" href="small-device.css" type= "text/css" rel="stylesheet" />

Safari是支持media选择的,only screen声明该CSS仅用于屏幕显示(不用于打印),同时Safari还支持max-device-width这样的选项,限定屏幕宽度小于480的设备才采用该样式表,这就把iPhone与桌面浏览器划分开来了。

另一个做法是在服务器端就判断当前浏览器是否是iPhone上的Safari,从而选择返回哪个CSS文件,这可以通过user-agent进行判断,iPhone的如下:

Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3

判断了浏览器之后,就可以开始针对Safari编写CSS了。Safari比较爽的一点是对CSS3的支持,设计PC端网页时总是要兼顾那个“后进”的IE,从而避免使用CSS3,或者需要确保IE在无视CSS3规则后仍然正确显示,现在不用为此而头痛啦,只要是Safari支持的就能用,针对一个浏览器设计就是比针对一堆浏览器设计要爽!

CSS3

接下来我们就列举一下Safari支持的CSS3属性吧:

边框

背景

色彩

  • HSL - HSL色彩
  • HSLA - 带Alpha通道的HSL色彩
  • opacity - 透明度
  • RGBA - 带Alpha通道的RGB色彩

文本特效

用户界面

  • -webkit-box-sizing - 可以让盒模型变得基于边框而非内容
  • resize - 让用户可以更改盒的大小(iPhone上不支持)
  • outline - 设置盒的外边框

选择器

Safari支持大部分的CSS3选择器,你可以自己使用桌面端或iPhone上的Safari打开CSS Selectors Test进行测试,桌面端与iPhone上的Safari测试结果是一致的。Safari完全通过测试的CSS3选择器包括:

*, E, .class, #id, E F, E > F, E + F, E[attribute], :link, :visited, :before, ::before, :after, ::after, :first-letter, ::first-letter, :first-line, ::first-line, E - F, :root, :not(), :target, :enabled, :disabled, :checked

其它

  • media - 根据media选择性加载CSS
  • multi-column - 内容分栏支持(iPhone上不支持)

还有一些Safari自己做的CSS扩展没有列举出来。Safari完整的CSS属性支持列表请看这里:Supported CSS Properties

表单

Safari支持对<input />元素比较高自由度的CSS定义,例如这样的:

实现所需的CSS属性上面已经列举了,所以不再详细说明,灵活运用就是了。值得注意的是,某些<input />的默认背景并非纯白色的,而是半透明的,这使得这些元素能够更好地和不同的背景颜色进行颜色混合。为了说明这一点,请先看白色背景下的示例:

然后再看看蓝色背景下的:

小结

CSS相关的话题也说完了,很简单是不是?大部分针对桌面浏览器进行符合Web Standards页面设计的设计师都能轻易掌握这部分内容,然而这只是实现设计的手段,到底如何设计一个界面,在iPhone上才算是拥有高度易用性的,这才是真正的问题。这个问题我们将在以后探讨,至少现在你能够将你自己的设计在iPhone上准确无误的实现出来了,那么我们下一步就进入JavaScript的讨论环节了。

本iPhone Friendly专题的订阅地址如下:

编写 iPhone Friendly 的 Web 应用程序 (Part 3 - XHTML)

在接下来的两篇文章中,我们将探讨iPhone上的Safari所支持的XHTML与CSS,之后才进入JavaScript的讨论。作为一款现代化的浏览器,Safari当然是基于标准的,那就让我们看看Safari支持哪些标准吧:

  • HTML 4.01
  • XHTML 1.0
  • CSS 2.1 以及部分 CSS 3
  • JavaScript (ES3)
  • DOM (Level 2)
  • AJAX (XMLHttpRequest)

熟悉这些标准并且平常也坚持Web Standards实践的朋友估计要笑出来了——就这些吗?我们天天在用啊,还有必要专门写文章来说明吗?事实上,Safari之前作为一款无PC版的浏览器,一直用户数量就不高,因此对它的研究也就不多,然而Safari其实有不少自己的扩展,因此还是很值得研究的。既然我们是针对iPhone设置,其实就是针对Safari设计,无需考虑兼容其它浏览器,这时候为什么不好好利用这些扩展增强自己的应用程序的可用性呢?

好吧,不说废话了,进入Safari对XHTML支持的介绍吧!

链接

iPhone对一下这样一些链接有特殊支持:

邮件

传统的mailto:地址iPhone将能自动使用内含的邮件程序处理,直接进入编写邮件的界面。完整的mailto:格式请参考RFC 2368

电话

iPhone上的Safari会自动对看起来像是电话号码的数字串(包括已经加入连字符或括号格式化过的)添加电话链接,点击之后会询问用户是否想要拨打该号码。如果你不希望开启这个自动识别,可以将它关闭:

<meta name="format-detection" content="telephone=no" />

如果你关闭自动识别后,又希望某些电话号码能够链接到iPhone的拨号功能,那么可以通过这样来声明电话链接:

<a href="tel:13800138000">13800138000</a>

Google Maps

Google Maps的地图链接会自动调用内置的Google Maps客户端软件打开,而非在浏览器内浏览。链接可以是一个地点查询:

<a href="http://maps.google.com/maps?q=cupertino">Cupertino</a>

也可以是一个路线查询:

<a href="http://maps.google.com/maps?daddr=San+Francisco,+CA&saddr=cupertino">Directions</a>

YouTube

如果链接是指向YouTube视频地址的,将会自动调用内置的YouTube客户端打开播放。能够识别的YouTube地址格式为:

http://www.youtube.com/watch?v=<video identifier>

http://www.youtube.com/v/<video identifier>

其中<video identifier>替换为视频的id。

图片

由于用户浏览时有可能使用Wi-Fi,也有可能使用EDGE(GPRS),因此你必须优化你的图片以确保即使是在使用EDGE访问你的网站的用户也能流畅的打开页面。因此你必须优化页面上的图片,尽量减少它们占用的传输带宽。另外Safari本身还对图片有如下的限制:

  • GIF(包括GIF动画)、PNG与TIFF解压后的体积小于2m。意思是,原图的长度乘以宽度再乘以每一个像素的位数,得出来的大小要小于2m。
  • JPEG解压后最大的体积是32m。解压体积大于2m的JPG会被进行二次抽样,最终显示给用户的是二次抽样后的结果。这使得Safari能够显示数码相机直接拍摄出来的照片,但显示时实际上是降低了精度的,以提高程序的执行效率。

为了尽量提高效率,例如你要将100*100的图片显示为10*10,就要在服务器端执行压缩操作,而不要直接用10*10的<img />来引用100*100的原始图片。

表单

text, password, textarea

在设计文本框的时候,必须记住用户点击后会出现软键盘,这时候可视区域就减少了:

另外在文本框中输入时,iPhone提供了自动大写与自动更正两项功能。自动大写的意思是,在输入开始的时候,以及在一个句号并空一个格后,自动会启用shift,输入一个字母后该shift自动消失。自动修正的意思是,iPhone会自动根据词库,包括自带的以及从你过往输入分析而来的,来对你的输入进行自动更正。我们都知道用手指点击那么小一个软键盘很容易误按旁边的键,这时候你可以不用忙于修正,只要iPhone提示的自动修正的词正是你想要的,你就可以按空格然后输入下一个词,iPhone会自动修正前面那个词。

要关闭这两项功能,可以通过autocapitalizeautocorrect这两个选项:

<input type="text" autocapitalize="off" autocorrect="off" />

select

必须留意到的是,iPhone上的<select />以不同的方式显示,以便于用户操作:

upload

iPhone不支持任何的文件上传下载,因此<input type="upload" />总是会显示出来,并且是……disabled的!

多媒体内容

iPhone上支持显示的多媒体内容,除了图片还包括QuickTime音频视频以及PDF。

如果需要创建视频,可以使用QuickTime Pro。如果你正在使用一台MacBook或者iMac,并且已经将QuickTime升级为QuickTime Pro,可以马上就试一试!你需要做的就是打开QuickTime Pro,然后开始录像,(接着请对着镜头傻笑3秒,或者做点别的),最后选择"Export for Web"。其中iPhone格式与iPhone (cellular)格式分别适用于Wi-Fi与EDGE环境,iPhone在播放时会自动根据当前的环境选择适合的流媒体文件进行下载与播放,另外poster是指影片播放之前在页面上显示的那一帧静态图片。导出后文件夹里ReadMe.html说明了如何将这些文件添加到XHTML中。

(事实上,导出结果中QuickTime控件引用的那个mov文件不包含任何视频数据,它只是一个引用,类似我们编程时所说的引用,用于指向真正的视频文件。不过这个引用指向的是多个视频文件,客户端根据当前的状态自动选择正确的视频文件来播放。)

其他琐事

虽然说是琐事,但也必须注意到,这样才能让你的页面更好地受到Safari的支持。这包括:

  • 声明正确的doctype
  • 避免使用frameset
  • 每一个独立的资源文件,HTML、CSS、JavaScript、以及非流媒体的其他多媒体文件,限制在10m之内
  • 顶级入口的JavaScript执行时间限制为5秒,超时将自动终止。
  • JavaScript分配内存上限为10m。
  • 同一时间最多在Safari内打开8个子窗口(同时浏览的页面)。

小结

XHTML部分到这里就讲完了,可以看出iPhone对XHTML的支持与桌面端的Safari是类似的,只是加入了更多扩展功能而已。下一期将开始讲CSS,欢迎订阅:

编写 iPhone Friendly 的 Web 应用程序 (Part 2 - Viewport)

在了解到iPhone的一些常见布局法后,我们就可以开始着手编写一个真正能在iPhone上跑的页面了。小声说一句,之前我说要布局讨论完了,要进入交互逻辑开发,后来细心一想发现不行,有些东西不讲的话将会对布局带来问题,绕过去的话并不怎么优雅,因此继续讲布局。

首先要说的就是viewport,也就是可视区域。对于桌面浏览器,我们都很清楚viewport是什么,就是出去了所有工具栏、状态栏、滚动条等等之后用于看网页的区域,这是真正有效的区域。(无论你屏幕多大,如果你装足够多的toolbar,你的viewport最终也会消失掉。)在桌面浏览器中,viewport的大小是与浏览器窗口大小直接相关的,窗口大了viewport自然就大,同时随着viewport的改变,页面布局可能也跟着变。例如width: 100%的页面宽度就总是和viewport宽度一致。

然而iPhone的Safari不是这样理解viewport的,它基于viewport呈现页面,然后用户缩放页面后viewport保持不变,仅仅是页面内容按比例缩放了。举个例子,在不设置viewport的情况下,默认viewport为宽度980(单位是像素),这时候页面的呈现出来的布局和在桌面短viewport宽度为980时呈现的结果一致,然而因为iPhone屏幕宽度为320,因此按比例缩小了。因此,一张宽度为320的图片,在默认viewport下会这样显示:

可以看到,图片按比例缩小了,这对于传统Web页面直接在iPhone上面显示来说是很好的事情,因为如果传统Web页面在980宽度的桌面浏览器viewport中显示正常的话,iPhone上显示也绝对正常。然而这对于Web应用程序来说则不是好事,因为我们需要按照980宽度来设计将来会以320宽度显示的页面,一个应该显示为320*80的元素,必须设计为980*245,这多麻烦!

因此我们需要改变viewport,让它变成这样:

实际上应该怎么做呢?我们有几个选择,因此先让我们看看到底我们能够设置哪些属性吧。我们可以操作的属性有4个:

  • width - viewport的宽度
  • height - viewport的高度
  • initial-scale - 初始的缩放比例
  • minimum-scale - 允许用户缩放到的最小比例
  • maximum-scale - 允许用户缩放到的最大比例
  • user-scalable - 用户是否可以手动缩放

这6个属性,我们可以设置其中的一个或者多个,iPhone会根据你设置的属性自动推算其他属性值,而非直接采用默认值。这点很重要,在完全不设置的时候有默认viewport,在你设置一个属性后其它值是自动推算出来的,不再是默认的。

如果你把initial-scale=1,那么widthheight在竖屏时自动为320*356(不是320*480因为地址栏等都占据空间),横屏时自动为480*208。

类似地,如果你仅仅设置了width,就会自动推算出initial-scale以及height。例如你设置了width=320,竖屏时initial-scale就是1,横屏时则变成1.5了。

那么到底这些设置如何让Safari知道?其实很简单,就一个meta,形如:

<meta id="viewport" name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>

在设置了initial-scale=1之后,我们终于可以以1:1的比例进行页面设计了。下一步我们就可以正式进入页面布局的细节设计了,如果你想继续关注iPhone开发话题的话,欢迎订阅:

2007年12月23日星期日

编写 iPhone Friendly 的 Web 应用程序 (Part 1 - 布局入门)

用过iPhone的朋友应该知道,iPhone上面的一些应用程序是能够随机器转动自动适应的,也就是说竖着拿的时候就竖着显示,横着拿的话就横着显示,iPhone中至关重要的Safari浏览器当然也支持这一点了,因此我们考虑设计iPhone friendly的应用程序时,首先要考虑兼容这种情况,不能把页面定死在一个宽度上。且慢,我们不是说设计自己的应用程序吗?这和内置的Safari有何关系?iPhone被设计为不允许安装任何第三方应用程序(破解不在讨论范围之内),一切第三方应用程序必须以Web的形式来跑,小到一个国际象棋的游戏也如此,因此我们现在说的应用程序就是指Web,但是与传统意义上以提供信息为核心的Web又不同,我们所说的是以提供交互操作为主的应用。

好吧,在我们正是进入布局的讨论之前,先来赏析一下已有的iPhone应用:

Facebook iPhone Edition

Facebook的iPhone版。如果你已经习惯了在iPhone上使用过Facebook,第一次在PC上浏览这个页面会被它的“肥大”吓坏的。从这个页面我们能够得知,让页面自动适应iPhone屏幕的方法就是尽量使用百分比来定义宽度,特别是全页宽度一律用100%,如果是导航栏里面4个项目并排的就每个25%。

AppMarks

AppMarks可以说是一个应用程序的书签,当然也有人把它作为Safari的首页,那就相当于桌面了,因为你收藏的应用程序就在这个页面直接显示,以大图标的方式。AppMarks以前是可以直接在PC上浏览的,现在已经自动将非iPhone的请求重定向到介绍页了,不过这里有一个AppMarks Demo能在PC上看看。我们能够看得到图片是4个一行地排列的,共12个,然而横屏会怎样了?当然是变成6个一行,仍然能够在一屏内显示完,并且不会有不满行的图标。其实这是一个很tricky的做法,12是4和6的公倍数,因此虽然竖屏和横屏的显示方式不一样,但你不会觉得有什么缺陷。

Newsgator Mobile iPhone Edition

终于有一个ASP.NET写的iPhone应用了,其实和上面的Facebook看起来差不多,同样采用了宽度为100%的做法,同时页面上的元素要么向左对齐要么向右对齐。这听起来很废话,其实意思是,中间尽管留空,不要想把整个宽度为100%的块区都利用起来,说明性文字可以放左边,操作及视觉反馈放右边,这样无论屏幕怎么旋转都好看。如果中间塞满了内容,只会让Safari进行比例缩放操作,把页面整个缩小,这其实是不利于操作的。

GMail

这不是普通的GMail吗?怎样才能看到iPhone版?这就需要通过修改user-agent属性欺骗它了。iPhone上的Safari所用的user-agent如下所示:

Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3

使用Firefox的User Agent Switcher修改一下user-agent就行了,然后你就能看到iPhone上看到的GMail了。仍然和上面的网站一样,可点击区域是一大个宽度为100%的块区。

小结

实际上,设计iPhone friendly的界面,在CSS的层面上来说一点也不难,甚至可以说是非常简单,因为基本上就是宽度为100%的块区,少数时候要用到按百分比划分的块区,但实际上我们也应该避免这种情况。为什么呢?别忘记了,iPhone的用户是没有鼠标的,他们必须通过手指来操作页面上的一切,因此可点击区域必须尽量大。

既然他们看不到鼠标指针,你也就不用考虑可点击区域必须是链接或者cursor: pointer,因为用户无法通过鼠标指针的改变来判断一个区域是否能点击。然而这同时也就引入了另一个问题,如何让用户了解一个区域能否点击呢?这时候你必须给出明确的视觉暗示,例如看起来是链接的文本,蓝色的文本有无下划线通常都挺诱惑人去点,这方面Facebook是一个例子。另一种做法是让界面看起来是菜单式的,好像AppsMarker和Newsgator那样,菜单右侧的符号让人挺熟悉不是吗?只要我点击这个菜单项,就会展开下一级菜单。最后一种做法就是基于用户已经熟悉的独创暗示来提示用户,习惯使用GMail的用户一看到邮件标题那个大大的蓝色区域,就知道点击是打开邮件,这是不需要任何指引的,最坏的情况下,用户不知道点击什么还是会点击邮件标题,之后他也就会用了。

到此为止,就已经把设计iPhone friendly应用界面的一些布局因素解释清楚了,我们下一步就要进入交互的环节,让我们的应用程序真正执行起来,并且是在客户端JavaScript环境中执行起来。如果你想继续关注本话题的话,欢迎订阅:

网络使用时间积累可能导致时空感扰乱

有时候会回想,到底大学三年干了什么呢,然后发现好像很难想起来,特别是后面那年。原因?我认为和花了多少时间对着电脑有关。

如果你每天有很多时间接触人,那么你的印象会很清晰,今天见到了这个人,昨天见到了那个人,之间的交互都是明确不同的。然而在线挂着就不同了,每天的东西看起来是很有建设性的,读feed增加资讯积累,写代码提高纯熟度,然而因为每天的动作都差不多,时空感很快就开始衰退。到底这件事是发生在昨天还是前天呢?我们是上个周末见面的还是再上一个周末呢?

我正在尝试观察这种现象,看看身边的人,特别是那些严重依赖于网络而且平时生活没什么特别的人,是不是很容易产生时空扰乱。同时我也尽量避免自己陷入这种问题当中,尽量确保自己每一天都是唯一的一天,通过特殊的事件作一个唯一的标记,这样我才能清晰记住某一个事件到底发生在哪天。

如果你对这个问题有所体会,或者有什么见解,欢迎留言。

2007年12月17日星期一

Apple 的帮助比 Microsoft 的要好多了

作为一个MacBook新手,当然很多操作都很不习惯,总是想用Windows平台的做法去完成一些任务,然而却发现做不到。最简单的,如何为一组mp3设置album photo呢?我尝试选中它们,但是直接在播放时设置album photo仅仅设置了当前播放拿首。然后我就查看帮助了,原来要打开info来改,这样才能实现批量修改。

总体感觉而言,Apple的帮助要比Microsoft的准确而且使用得多,当我想要做某个操作时,我总能找到有效的帮助,并且帮助上的信息也是确实可操作的,不会操作到一半发现有问题卡住了。从这点上看来,还是Apple的东西可用性比较好。

2007年12月15日星期六

iPhone + MacBook

终于把iPhone拿到手了,也就终于有设备能够和我的iTunes同步了,podcast才实现了其真正意义——自动下载,自动同步,无需人手干预。另外,iPhone的照片也都发到Facebook了,想看的可以过去看看。

使用了一个星期的MacBook之后,慢慢开始习惯了,反正现在不就是Web平台的时代嘛,什么东西不能在Firefox里面解决?OS上缺少什么软件也无需太在乎,有Web就好。

其实同时使用MacBook与PC最大的问题就是快捷键不适应,整天会想在PC上按command键,然后才发现在手上的是PC。然后用了几个小时的PC之后,再换回MacBook,同样的问题又出现了,又要重新习惯MacBook的键盘。

有一点比较怪异的就是,MacBook对我的移动硬盘还是不太“适应”,不知道是不是Spotlight索引的时候要锁住硬盘,反正硬盘刚刚插进去会自动卸载,然后又再加载。

最后要说的就是,好像我最近发blog都是那么短的,完全没有什么深度的,应该发到Twitter的那种类型。事实上,最近实在太忙了,除了公司的工作以及翻译的工作,还要忙于玩MacBook和iPhone呢,没时间去思考什么了,咔咔。

2007年12月9日星期日

使用 fluid layout 时记得设置 min-width

希望aw不介意我拿他的blog来做例子,因为第一次想到这个问题是我在手机上看aw的blog时碰到的。我的手机屏幕小,然而Opera Mini运行在完整视图时会以贴近Opera PC的形式处理CSS,因此fluid layout的多个列不会自顶向下顺序显示,而会保持原来并排的布局,同时因为fluid layout没有强制width,因此Opera Mini就会使用手机浏览器的宽度来显示整个页面,可想而知原本一个列会被压缩到多么的窄。

因此,使用fluid layout时,要加上min-width属性,加在哪个具体元素上看具体情况而言,但是凡是你不希望被缩得太窄的列,你都应该加上min-width属性。这时候,Opera Mini会发现无法压缩页面宽度,就只能显示横向滚动了,不过这看起来至少会好一些。

2007年12月8日星期六

MacBook 到手了!

梦寐以求的MacBook终于到手了,并且已经装上了最新的Leopard。因为是托熟人到香港买的(总害怕自己带过关会有问题),因此所谓的“第一次开机”已经看不到了,语言也选好是简体中文了。对于我这样有洁癖的人来说,哈哈……第一件事当然是重装,并且最近什么都装英文版,因此干脆装英文版好了,反正Mac OS的多语言支持是与系统界面语言无关的。

界面真的很漂亮,可惜看了说明书也不知道怎么用……我的意思是无法好像Windows那样灵活使用起来。什么文件要怎么用,都不知道,只能自己慢慢摸索。我在写这篇帖子的时候,机器还处于漫长的Mac OS安装过程中,因此也没什么好说的。

至今为止,我小小的一个办公格子中,就已经有三台PC了,真是有点点壮观,于是立即拿DC拍了下来,传到Facebook作为留念。如果大家想和我交流MacBook使用经验,那是绝对欢迎的事情。

2007年12月7日星期五

Facebook 真正的重点在于 News Feed

其实我一直就觉得Facebook的news feed做得很不错,但是又说不出有什么特别。直到有一天,Jeffrey Zhao问我如果要实现一个Facebook的news feed我会怎么做,我才开始认真思考这个问题。这个news feed说起来就像是Google的搜索结果排名一样,你不可能用一些简单的规律来描述,但是你总能得到有用的信息。

首先,Facebook的news feed可以说是各好友的mini feed的聚合,但这又不是简单的聚合,否则就变成毫无意义的flooding了。暂时而言,我只能看出这种聚合反应的是一种趋势(trend)。例如有一天我看到我有六位好友换头像了,显然不可能一天之内发生这样的事情,只能说几天的时间里好友更换头像的事情聚合为一条新闻了。几天之后,这条新闻不但没有消失掉,还变成了八位好友更换头像的新闻了,最后变成了九位,前后总计在我的news feed上停留了一个星期。

虽然对我而言,更换头像就算是一个trend也没什么意义,我不会去跟风换个头像,更不会消费什么。然而有些trend是能够带来消费的,这时候把trend聚合出来,就能吸引更多同类消费了。例如现在比较成功的apps吧,如果有若干同好的好友用了同一个app,那么我就会比较好奇那个app是干什么的了。因此,将来如果Facebook有了更多涉及资金流的apps,news feed将推动好友之间的资金一同流动。

最后,预测一下技术的发展,所谓的PeopleRank估计已经有不少网站在研究了,其实大家都想知道如何去评估一个人的价值量,或者说以这个人为核心的信息的价值量,只是怎样才算是成功的算法,还有待验证。

2007年11月30日星期五

SNS 的世界会变平吗?

我们都听说过“世界是平的”这一说法,全球化无所不及也难以抵挡,那么在SNS上又如何?

今晚去北航听了王兴关于创业与SNS的讲座了,他提到的一个说法就是“仿生”。不是真正的仿生物,而是在线服务一般都仿照一个现实中存在的服务来做的,例如新闻门户是仿报业集团,购物网站是仿超市,C2C网站是仿跳蚤市场……他说SNS就是仿城市,一个城市里面,什么服务都有,上面的都包括了。

那么既然你已经看到城市这一步了,我就肯定要想知道下一步是什么。其实这个发展过程很像人类部落发展到大城市的过程,一开始有一些人定居,有贸易,由公共服务,就成为小部落了,然后再慢慢发展到大城市。至于API逐步开放的过程,王兴说的和我想的也差不多,开头是计划经济社会,城市里面所有服务都是官方提供的,然后逐步开始私有化,最后变成第三方竞争的提供服务。

那么到底现代城市建成之后,下一步如何呢?那就是城市之间的互联互通,以及建立新城市的成本会降低。互联互通方面,我觉得OpenSocial API可以说是潜在的UPS,全球化的物流枢纽。以往你在一个城市里住,就接受它的一切了,服务和文化之类的,没得选择,就好像我跑来北京实习就必须接受它的一些现状。然而客运与物流能力的提升,肯定能够消除这些限制的,让你不再需要考虑这个城市有什么,只要一件物品存在于物流网络中,你就能够要到它。

在这里必须强调OpenSocial API与SNS单方面开放API的差别。单方面开放API只是说明外部商业可以进入,然而OpenSocial API为连锁商业提供了标准,一个标准之下能够在多个城市提供服务。

与此同时,Ning这样的平台为建立新城市或者说新城区提供了便利。既然UPS已经能够使得城市间无缝连接,那么住哪里不一样?不一样的其实也就是与谁一起住,除此之外也没什么需要选择的了。天时、地利、人和,暂时而言,成功的SNS得到的是人和,这是一个完全的人为因素,然而完全不受人控制的天时,以及只能局部选择的地利,对于SNS来说又是什么呢?这可能将会成为决定人们定居的真正选择因素。

2007年11月27日星期二

转会咯,从广州转北京咯!

我说的是从广州.NET俱乐部转到北京.NET俱乐部,哈哈。今天一早的飞机抵达北京,明天入职开始在百度为期三个月的实习。非常幸运的是,11月刚刚参加完广州俱乐部的活动,到了北京后就马上碰上了12月的北京俱乐部的Visual Studio2008 & Windows Server2008 交流会。如果你是北京俱乐部的成员,或者好像我这样“碰巧”有机会参加这次活动,那么到时候见咯。

2007年11月25日星期日

网络游戏:强迫不同玩家按同一个方式玩游戏

我不玩MMORPG,最近唯一沉迷于地Web Game是Travian。玩了半年的Travian之后,开始对网络游戏所谓的反作弊措施有了个感觉,那就是强迫不同的玩家按同一种方式玩游戏,以示所谓的“公平”。

为什么有这种感觉呢?这要从大陆Travian禁用GreaseMonkey说起。我之前虽然听说过有脚本可以增强游戏的UX体验,甚至提供众多自动化支持,然而却没有用过。事实上,一个GreaseMonkey脚本能做什么?就是自动化访问网页,自动化抽取信息并进行分析,仅此而已啊。真正的作用?就是原本你可以通过大量手工操作,打开大量页面或者需要人手计算的工作,变成它帮你做了,直接把结果显示在页面上了。

为什么这是不允许的?因为网络游戏就是要追求一种公平性,而且Web Game还特别强调这一点。总之,你有任何聪明才智都不能用于制作自动化工具,必须手动完成所有工作,这就是我所说的强制性了。

事实上,不同的玩家希望以不同的方式进行游戏,这是很正常的。暂时我们先把游戏当作纯粹的娱乐方式,不涉及任何现实世界的商品交易或者其他利益得失,那么玩家对于娱乐的个性选择就应该是被允许的,然而这种个性现在被限制了。然而这种个性在单机游戏中确是完全开放的,例如NFS:Pro Street提供了三种难度,从全套辅助系统的Casual,到完全手动操作无辅助的King,以及两者中间的Racer,这使得几乎任何层次的玩家都能从NFS找到合适自己的玩法(比King还骨灰或者比Casual还弱的除外)。

暂时而言,网络游戏提供这种个性化选择的唯一方法就是分服务器,这台服务器是这种配置的,那台服务器是那种配置的,然而服务器之间是不能够沟通的,你也不能中途转服务器(除非一切重新开始)。单机游戏的话则无所谓,中间随时可以更改个性化配置,而且有些游戏对于拟真度的选项还超多,如果把这些选项组合都用独立服务器列举出来的话恐怕是绝不可能的。

总而言之,至今为止网络游戏还无法解决玩家对这种娱乐形式的个性化定制需求的问题。如果这个问题能够被解决,将可能挖掘更多的潜在玩家客户加入到网络游戏当中。

2007年11月24日星期六

TOEFL 成绩出来了

28+29+20+25=102,裸考的。不知道“裸考”是否是一个广泛被接受的说法,意思是完全不准备就去考试了。

显然,我曾经有半年的时间可以去准备TOEFL考试,但是我没有采取认识常规的复习方式,拖到考前几个星期,觉得压力太大了,干脆选择不复习了,祈求有80分就不管了。结果看起来还不错,口语异常低也算是预料之中的了,因为确实不知道能够如何提升,我说的是提升真正的口语能力,不是用pattern去应付考试中特定的题目。

我所谓的准备,就是保持着用英文思考,保持这种英文跟别人聊天,但事实上没有一个partner是真正的native speaker,所以效果只能说是保持,不能说是提高。有机会还是要找人keep住练,这样才能在将来的考试中做得更好。

2007年11月20日星期二

从 Adobe SHARE 说到 Silverlight 的 XPS 支持

在很久很久以前,我们仅仅知道Flash能够做一些JavaScript做不到或者做不好的交互,特别是复杂度高的交互,除此之外也没有什么理由使用Flash了。后来Flash加上了视频支持,却一直不受重视。然而突然有一天YouTube就出现了,人们才发现这项支持的真正价值,并且纷纷效仿。再后来,Flash还加入了对PDF文档的支持,这项技术最近已经被Adobe成功利用于开发Web2.0站点了,那就是Adobe SHARE

在Silverlight发布的时候,大多数Flash拥有的功能Silverlight都做到了(嵌入字体除外),当然也包括上述的视频与文档支持。只不过,视频支持变成了WMV,因此也就直接支持DRM了,而文档支持则变成了XPS。这个XPS支持能做什么?我们还是先来看看Adobe SHARE的示例文档吧,这个文档其实就是一个PDF,然而你无需安装任何客户端的PDF阅读软件,也不需要为浏览器加装任何PDF插件,就能够直接在浏览器中以Flash的形式阅读。

考虑一下,以往Sharepoint里面的文档要在浏览器中直接以只读方式打开阅读,可以转换为HTML版本再显示,Sharepoint内置了这一功能,然而这转换肯定会存在一定的失真。现在可以考虑转换为XPS了,然后使用Silverlight作为阅读器就可以了,和HTML版本一样不需要客户端安装有Office,然而用户体验却比HTML版本好多了,基本上不会失真。这XPS支持真的能做到吗?能。而且已经有人做了一个小小的演示,证明可以在web上面实现XPS Reader,请看:Simple Silverlight XPS Viewer

其实这次说到Silverlight的XPS支持这样一个“偏门”的话题,只是想说明很多RIA技术已出现,却暂时没有适合的应用场景,或者说官方给出的一些适用场景你觉得太狭隘了对你没有一点儿价值。举个例子,例如HD视频的支持,你可能会想这高清以现在的带宽谁享受得了,最多就是Microsoft和20st Century Fox合作搞个演示网站而已。实际上,我们真正需要的是一种创意,去想象如何使用这些还没有多少人使用过的技术去挖掘潜在的市场价值,正因为这些技术还没有多少人使用过,你一旦深挖就已经是专家了,你的领先地位就稳固了。

最后,大家发挥想象力想一想Silverlight现有的功能还能应用到什么有意义的场景中去吧,有想法的朋友可以直接在评论中留言哦。

2007年11月19日星期一

Silverlight 与 WPF 的一些差异

首先,这两者使用的namespace是不同的,不要以为都是<Canvas />作为根元素那就是同一个类,其实从XML语义的角度来说不同namespace的同名节点代表着不同类别的实体。Silverlight的默认namespace是http://schemas.microsoft.com/client/2007,而WPF的默认namespace则是http://schemas.microsoft.com/winfx/2006/xaml/presentation。有趣的是,WPF是不兼容Silverlight文档的,也就是说一个具备Silverlight namespace的文档用WPF方式直接打开XAML的话就会立即抛出异常,然而反过来做则是可以的。如果在Silverlight调用的XAML中使用WPF或Silverlight以外的namespace,则会抛出异常,这说明了Silverlight不是没做namespace检查,而是刻意兼容WPF文档的。

其次,这种兼容并不意味着完全一致的呈现方式,其实两者对于同样的XAML在呈现上是会有略微差别的。我现在发现的一个细微差异是关于StrokeThickness的“溢出”效果的。首先说说Rectangle吧,假设一个长宽都是100的Rectangle,而它的StrokeThickness从0向50增长,那么你可以看到其边框的变化是从无到有,从最细到完全填充整个矩形,在这个过程中边框的延伸都是向矩形内部方向的,没有对外溢出。然而如果StrokeThickness超过50继续增长呢?这时候WPF与Silverlight的表现就有所不同了,WPF表现为与StrokeThickness等于50时一致,没有任何变化;Silverlight表现为矩形的长宽自动扩大为StrokeThickness的两倍,以确保相对的两条边框是不会互相重叠的,因此矩形不断向外“溢出”。注意“溢出”是以矩形的中点为变换(Transform)中点(Center Point)的,也就是说向上下左右四个方向等距离延伸。

我们再来看看Ellipse,同样先创建一个长轴和短轴都是100的Ellipse,然后开始增加StrokeThickness,直到超过50。Silverlight的表现和之前的Rectangle一样,那就是向外“溢出”,不过变换中点变成了左上角,扩展后的圆形向右下延伸了。WPF则更奇怪一些,同样的“溢出”问题,不过根据原来100*100的区域进行了类似Clip(剪裁)的效果。也就是说,如果StrokeThickness为200,那么这时候圆的直径为200,然而因为Ellipse的长宽都设置为100,因此就剪裁出左上角100*100的区域,你只能看到左上角的1/4圆。

看到Ellipse的效果后,我们可以反过来解释Rectangle的,其实WPF中的Rectangle不是不“溢出”,而是“溢出”后经过剪裁就和没有“溢出”一样。我们发现了的Silverlight与WPF差异,也就在于“溢出”后是否对区域进行剪裁。

这时候使用Expression Design做设计的朋友就要注意了,因为Expression Design导出的XAML无论选择WPF还是Silverlight,其namespace都是WPF的,虽然都能正常打开,但在Silverlight中看到的效果可能就与你想要的效果有所差别。

最后,有兴趣关注Silverlight的朋友,欢迎订阅我的blog:

2007年11月18日星期日

Google Maps for Mobile

今天在Read/WriteWeb看到5 Essential Mobile Web Apps,这是根据上次Tell Us Your Favorite Mobile Web App!评选出来的,里面有Google Maps for Mobile,而且从界面看起来像是很轻量级的Java版。刚开始仅仅知道这东西有BlackBerry版,后来在Palm上通过wifi用过Java版,而且好像还不支持代理必须二进制数据流直接连接,因此从来没有想过在手机上用。我的手机仅仅开了cmwap,只能经过HTTP网关(其实就是HTTP代理)访问Internet,不支持直接的二进制数据流。现在又看到它的名字了,自然要去下载一个到手机再试试,结果发现可以用了!

开头我去英文网站(.com)下载了个英文版,发现在中国没什么值得看的,因为仅仅提供公路图不提供鸟瞰图。后来想起中国的地理数据是不允许“出口”的,因此Google Maps本身就与中国版本的Google地图脱离开来了,随之去中文网站(.cn)下载了个中文版。中文版的功能当然和Google地图一样啦,另外Google地图做了的mashup(例如搜索结果中的餐厅能看到dianping.com上的信息)也在移动版上体现出来了,因此真的很好用。暂时而言,我觉得这东西用于出行时随身寻找餐厅还是很有意义的。

2007年11月15日星期四

现在的游戏体积越来越庞大了!

一个20G的分区,装两个游戏(连带资料片)就装不下第三个了,什么道理。游戏载体都从CD变成DVD,从D5变成D9了,动不动就占用硬盘6G到9G的空间。看来以后玩游戏,还是要有个相当大的游戏分区哦。

2007年11月12日星期一

我希望 IM 能够自动清理好友

我们都知道,有些关系是长期的,有些关系是短期的,然而IM上面添加了的人就是长期的,这是很不与时俱进的一件事情。

两个人之间的关系是随着时间发生变化的。有些人我开始并不太信任,我会希望放在一个次一级的区域,例如他看不到我是否上线,除非我主动联系他,他也不能看到我其他联系方式,然或随着合作的增加而把这个人放进普通朋友一栏。有些人则是因为一次两次的合作而添加了,之后再没什么来往,我对于他是否上线不感兴趣,这没必要每次都提醒我,我希望若干时间不联系后IM能够自动把他给“存档”了,仅在日后搜索结果中出现。

IM不好的地方,在于强迫你对所有好友一视同仁。而现实中当然不是这样的,你对某些人更关注一些,而对另外一些人不太关注,最后你还希望没有人知道上述区别,好像他们每一个都是你心目中的VIP。IM作为一种工具,应该帮助我们做到这一点,而不是让我们自己通过work around去解决这个问题,例如使用多个IM加不同类型的人之类的。

2007年11月8日星期四

ActionScript3 与 JavaScript 的对比在哪?

最近在看《Flash ActionScript 3 殿堂之路》,觉得作者还是非常专业和非常用心的。

说作者专业,因为这本书的作者不仅仅能够在书中指出没有程序设计经验(特别是没有OO经验)的新手应该注意的事项,还能指出一些ActionScript3与Java以及C#的区别,以便于Java或C#程序员更高效的利用这本书。因为对于从Java或C#迁移过来的程序员而言,他们有丰富的OO经验,需要学习的仅仅是语言之间的差异以及新的框架与库。

不过我想说的是,为什么没有ActionScript3与JavaScript的对比呢?它们都是ECMAScript,都为前端开发人员所用,一个是Flash/Flex方向,另外一个是Ajax方向。就我个人而言,我有相当的C#与JavaScript经验,然而ActionScript3与JavaScript更相似,我当然是希望拿JavaScript作对比的。ActionScript3与C#作对比只能停留在一个比较表面的层次,要说到关联数组、函数对象这些只有JavaScript才能作对比,C#根本没这些概念。

另外书中关于ActionScript3与Java以及C#的对比,也停留在很表面的编码方式的对比上。作者很强调这本书是“面向原因(why-oriented)”的,因此在ActionScript3的教学方面更倾向于揭示本质而非介绍编码方式。然而我并不确认这是因为作者对Java以及C#的认识仅仅在于编码方式的层面,还是他认为揭示Java以及C#本质与ActionScript3本质的差异就扯得太远了,反正这本书缺乏这方面的内容。

我举一个例子,书中讲到ActionScript3的基本数据类型是immutable的,因此虽然还是以引用方式存在,但是一旦要改变其值就不是在原对象上作改变,而是新建一个对象保存新的值然后把引用指到新的对象。书中的对比并没有提到C#中的string其实也是immutable的,事实上任何一个对.NET CLR本质有一定了解的人都知道string是通过immutable来模拟值类型特性的,这是和ActionScript3的做法比较类似的。更深入的知识,例如ActionScript3是否也像Java或C#那样子使用拘留池(intern)来为字符串对比提供便利,这书里面好像也没说到(全书我还没看完)。例如((new String("abc")) == (new String("abc")))的结果是true还是false呢?这就涉及到拘留池的问题了。

说作者用心,是因为这本“面向原因”的书确实为所谓的“语言迷”(就是很喜欢研究语言风格以及底层实现的人)提供了深入学习ActionScript3的途径,同时还顾及了其他各种读者群体,例如完全的编程新手,或者是从其它OOP语言迁移过来的程序员。

说实在的,我觉得这样一本书对于语言迷而言更有善一些,因为我一翻开书就能够确定“嗯,这个作者很pro,至少他是懂很多东西才来写书的,不像某些国产书那样作者完全是不懂装懂的”,然后我会确定我想要读这本书。对于新手而言,可能还是“案例导向(case-driven)”的方式更好一些。因为我自己也时常考虑写书的事情,想着如何将自己的经验浓缩为case study,然后发现这并不是一件简单的事情,因此挺佩服国外那些“案例导向”的书的作者的,特别是Manning的In Action系列的书。

事实上,虽然作者写了新手可以跳过某某章节,然而对于如饥似渴的读者而言,又怎么可能随便忽视掉书上的知识。不过确实有看不懂,那怎么办?那就比较容易让人有挫折感(frustrated),这就不太好了。“案例导向”的好处就是,即使读者基础不好,他跟着case一步一步做下去,或发现case的应用程序的功能逐步完善,这样就有成就感了。就算他连打字都会打错吧,但他总能够直接打开源代码查看两个version之间的差异吧,一看就发现果然如书上所说稍微用一点新技术新version上的功能确实就比旧version的要好了,这样他就信服了。

无论如何,这本书都算是一本高质量的国产书,也算是国内第一本深入讲解ActionScript3的书籍,因此我很乐意在豆瓣上给它五星。如果将来翻译为英文版发布,我也会去Amazon写review给它个五星,哈哈。

P.S.大家还可以参考一下aw对此书的书评

Recruiting Events: Baidu (Part 4)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

布丁告诉我,Baidu是没有专门的HR面的,不过三面看起来有点像是HR面。面试的mm问了我很多很细节性的问题,例如有没有给人提过建议、最近团队合作解决的一个问题、难相处的人等等。这些问题都不与特定的技术相关,虽然有些问题回答时可能要撤上技术解释,而且看起来是以早准备好问题列表的,因此说像是HR面。

我的自然感觉还好吧,那么多个问题下来几乎说了一个钟,比Tencent的那个HR面长多了。我觉得这次的问题都比较好答,没有什么是不希望告诉她的,因此也就不存在说多了的情况吧。至于什么时候有结果,布丁说他也不知道,不过他说Baidu一般就是两次或三次面试。

2007年11月6日星期二

Facebook 与 hang out

之前曾经在一个用英文编写的中国web2.0观察blog上看到一个观点,解释为什么国内没有出现Facebook现象,也就是一个大的social network聚拢了绝大多数用户。它的解释是,Facebook提供的服务其实在线hang out,它真正入侵了的市场是传统的hang out场所(例如pub),总而言之,这算是一种娱乐服务。基于美国的Internet一直围绕着信息与商务发展这一特性,Myspace和Facebook跳出来提供娱乐服务自然能够抢占到相当的市场份额。然而中国的Internet一直在信息与商务服务方面发展得不怎么样,反而娱乐服务是过度供应了,因此无论你推出怎样一种全新体验的娱乐服务,都不可能在娱乐服务的整体市场中抢去大份额。

实际上说到hang out,就必须提及我之前听说过的一个理论,也就是大学生为什么那么喜欢玩Counter Strike,原因当然是这也算一种hang out的方式。不过这是几年前的理论了,现在玩CS的人应该没那么多了。校内网成为一个hang out新去处,却做不到Facebook现象,不过应该比之前的CS要好——毕竟大多数女生愿意上校内网但不会玩CS。

那么为什么比Facebook更具备social功能的LinkedIn以及比CS更具备联机游戏能力的Battle.NET不能成为大众hang out的选择呢?因为这两者的目的性都太强了,而hang out就是一群朋友聚在一起,享受那种什么也不做的感觉。如果目的性很强地去做一件事,那就变成teamwork了。为此可以再举一个例子,就是麻将和桥牌,一种可以作为hang out活动而另一种不可以。也曾经有人对麻将有种解释,说中国人之所以喜欢玩,是因为容易上手,再没有经验的玩家也可能碰到牛屎运,再配合我之前所说的“赢就是最大的兴趣”的理论,麻将当然受欢迎。桥牌则正好相反,必须专业学习,你的赢面才大,自然玩的人就少。

最后,我们需要观望的就是下一轮的hang out服务将会是什么样的。可能不再依赖于social network了,但总之那必然是一样很休闲的活动,谁都能体会到其中乐趣的。

2007年11月5日星期一

Recruiting Events: Baidu (Part 1, 2, and 3)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

今晚参加了Baidu的宣讲会,不过没有参加笔试,这篇文章算是一次性总结一下之间应聘Baidu的经验。首先当然要感谢布丁的内部推荐,让我在广州宣讲兼笔试前一个月就开始了Web开发软件工程师职位的电话面试。

第一次面试比较轻松,虽然也提到一些技术,不过就我个人感觉而言,还是吹水成分比较大。我觉得说说自己的项目这样的话题,都算是吹水吧,反正我是确实有经验和体会的东西。然后很少一点点JavaScript与CSS相关的细节,仅此而已。当然说完后,他说这算是了解一下情况,之后会再有面试,让我觉得这好像就是第零次面试。不过讨论的氛围确实也很轻松,就真的是了解情况那样,我也问了一下有没有可能去北京面试,他说需要在部门内问一下是否能如此安排。

第二次面试是在上个星期,还是电话面试,这次还是从做过的项目开始讨论,不过深入了不少,另外也问了我两道往年的笔试题。其中有一些地方答不上来吧,例如说到JavaScript中如何统计一个字符串中的字符频率,我想到得最好的办法就是把它打散为数组,然后排序,然后看每个字符连续的个数,那就是频率了。另外一个想法是,读取字符串的首个字符,用replace把这个字符串中的这个字符全删掉,看看字符串缩短了多少,这也就是频率了,重复此操作直到字符串清空。我觉得理论上时间复杂度最低的算法,也必须把每一个字符都扫过一次,也就是说字符串长度为n,那么时间复杂度最少就是O(n)了,至于JavaScript中怎么写,就看你把这个复杂度自己做了,还是让JavaScript内部操作了,例如排序就是让JavaScript自己按快速排序算法来承担了一个时间复杂度。

另外一个想不出答案的问题就是,CSS的两列布局,其中一列定宽另外一列不定宽。我记得我在网上看过解决方案,然而当然没仔细看,估计是收藏了就算那种,哎……我不知道电话面试中直接搜索是否是一个可行的策略,反正我选择了不搜索,思考不出来也就老实告诉他。之后,我在A List Apart找到了一个篇相关的文章:In Search of the Holy Grail。总的来说,我觉得第二次面试还算不错吧,当然这完全是个人感觉。

最后当然是今天参加的宣讲,其实仅仅去了解一下Baidu而已,例如得知了工程师的起薪是16万一年,这看起来比Microsoft的15万起薪要高一点点哦。不过后来有人解释给我听,16万是指上限,从来没听说过应届毕业生进去能够从16万开始拿的,反过来Microsoft那个15万是下限。

更新:布丁告诉我,那个16万是下限来的,并且不是狭义上仅仅软件工程师这一类别,而是广义上的工程师职位(例如包括我申请的Web开发工程师)。

2007年11月4日星期日

Recruiting Events: Tencent (Part 4)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

得知自己进了Tencent的三面,下午Microsoft笔试出来后急急忙忙就跑过去了。去到呢,发现原来没多少人在等,然后大家接着都到了,等候区的HR都先用同一个问题问候大家“刚刚有趣Microsoft或者中国移动的笔试吗”,结果大家都说是,汗……

不知道是不是我走路速度快,我前面竟然只有一个人正在面试,所以很快就到我了,而等我出来的时候等候区已经挤了一堆人。HR面其实很快的,基本上就三大问题:自我介绍、其它公司应聘情况与期望年薪。当然,第一个问题会深入一些,例如问问你觉得大学期间最成功的事情啊,或者你说说自己的优点缺点并附例子啊,之类的。问技术我可以吹水,但是这些我完全不知道对方是如何根据我的回答进行评估的,没有任何tricks可言,所以我暂时也不知道怎么评价自己的这次面试好。

暂时而言,我不确定自己是否说太多了,因为在别人面前我不擅长一言不发并且摆出让人难以捉摸的样子,就算我不说话,我的姿态也会有所表现,所以还是难以控制我向对方透露的信息,还不如保持说话好了。但是保持说话又可能说多,我这次就让她知道了我投了Google、参加了Microsoft并且准备看看Baidu,还说了更期望在广州工作,貌似还是泄露了挺多东西哦。

Recruiting Events: Microsoft (Part 1)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

下午参加了Microsoft的笔试,因为受到NDA保护,细节就只能掠过咯。其实昨天在Tencent面试等候区域,就碰到了一位参加了上一批次参加过Microsoft笔试的人,他说笔试分为四大部分:公共、编程、测试、策划。今天去到发现确实也就是这样,公共部分的选择题就是考基础的,C/C++/C#题目混杂在一起;编程、测试、策划分别考SDE、SDET、PM这三个方向,但无论报什么职位都要全做。

这四个部分的时间安排分别是50分钟、40分钟、30分钟、30分钟。地一部分我刚刚好50分钟做完。第二部分我做超了10多分钟,因为代码要用C++写,哎……不熟悉啊,允许C#的话就好了。第三部分狂赶了一下,然而第四部分也没足够的时间,只能随便答了一些。

总之,感觉自己对C/C++的熟练程度影响了答题速度,这是最劣势的一方面。另一方面,use your time wisely也做得不够好,前面选择题我懂得解不出(或者觉得没有一个答案正确的)就跳过,因为选错了还要倒扣分,浪费时间也和倒扣份差不多,但到了后面我就没有严格的时间控制了,但只编程部分浪费了大量的额外时间。

拼写检查(spell check)依赖症

我发现我已经有拼写检查依赖症了。以前还只有Microsoft Word这个级别的软件有拼写检查,输入几句话才不会去开个这样耗费内存的软件来检查有没有拼写错误呢。后来有些轻量级的软件或则web应用也引入拼写检查了,再之后浏览器都能通过插件实现全面的拼写检查了,这下就完蛋了——我已经记不住单词的正确拼写方法了,反正差不多就随便写,然后软件会帮我纠正。看来以后还是要多查字典,多脱离软件的帮助进行写作,否则就只能期望什么时候TOEFL考试软件也能加上此功能了。

2007年11月3日星期六

Recruiting Events: Tencent (Part 3)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

今天早上考完TOEFL,就被告知Tencent二面有我份,于是下午就又跑去了面试。被HR引导到休息区之后,发现我要见的面试官已经有两位同学在等了,等了很久里面那位才出来,之后就是漫长的等待……这位面试关平均40分钟面一个人吧。期间HR和我们吹吹水,有问到大家还去了哪些公司面试之类的,发现在等待的人当中也有不少是中大的。

轮到我面试的时候,发现这次终于不是吹水了(一面我觉得很吹水,但其他人都说很技术)。例如说了说HTTP协议的细节,面试官说很多用ASP.NET的人都不了解HTTP协议,我想这是肯定的吧,反正问HTTP协议对我来说是简单的事情。(只要不是好像上次那样给Mango突然问我一句“你知道什么是Partial Response吗”就好,其实我是知道Content-Range这一请求首部的,但不知道对应的响应代号应该是206而不是200。)然后讲到ASP.NET只会用于Tencent的内部应用,不会用于互联网应用,又问问我仅仅研究web是否太窄了,我就把Bean对iPhone式移动web应用的热情搬出来和他吹。

最后他考了我一道数据结构的问题,说明是很基础的,并且他对ACM不感冒。所谓基础,就是指hash,哈哈。hash的执行方式我当然答得上来,然后问我hash除了存储以外的作用,开头我想不到,提示密码学后想了一会儿终于明白他指的是md5这样的东西,然后又是吹水。接着他还问了有什么是二分搜索适合做的,而hash不适合做的,其实二分搜索的特征就是顺序存储,经过一些提示后我终于想到是例如要选择top 10元素就必须用顺序存储,但他说还要再简单一些。其实最终的答案是大小关系,我想他的意思是顺序存储要对比a[i]与a[j]的大小,直接对比i和j就是了。然而我当时脑袋里想的什么呢?我想,Object是一切对象最抽象的根元素,顺序存储就是说元素之间存在偏序关系……汗……其实大小关系就是一种特殊的偏序关系嘛,然而我的思维偏向于抽象的方面了,例如Object和偏续,实际上他想到的是实实在在的数据结构——就是说存储的就是纯数值。看来我是想复杂了,以后一定要弄明白当前对方的视觉,否则思想一致但视觉不同也会答不上来。

2007年11月2日星期五

Google 发布 OpenSocial API

期待已久的Google的“比Facebook开放得更彻底”的API终于发布了,名为OpenSocial,并且作为一个API项目在Google Code上发布文档。

Open Social API包括3大主要部分,分别用于操作个人数据、操作活动数据以及数据持久。暂时而言,这些API都标记为并未发布将来可能仍会修改,不过新闻稿几个小时之前已经发布了,而API文档刚刚也上线了。这些API都基于Google Data(GData)协议,使用Atom协议以及Atom发布协议进行数据的查询以及更新,因此只要使用统一的GData操作库或者Atom操作库,应该就能轻松地从第三方网站或者客户端访问Google的数据。另外与此相关的另一个重要的基础API就是Google Auth,它的用途是验证Google账号的登录。

这次Google和那么多大型的social network合作来做一个API,其口号也就是“一次学习到处编写”(怎么那么像“一次编写到处运行”呢),也就是说开发人员已经无需学习不同social network的API了,使用Google的OpenSocial就能够操作各个social network合作方的平台。现在,支持OpenSocial的合作伙伴包括如下的16个:Engage.com、Friendster、hi5、Hyves、imeem、LinkedIn、MySpace、Ning、Oracle、orkut、Plaxo、Salesforce.com、Six Apart、Tianji、Viadeo、XING。根据我个人的猜想,这些厂商应该是在不同的侧面以及不同的层次提供OpenSocial的支持吧。

2007年11月1日星期四

Recruiting Events: Tencent (Part 2)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

今天下午去参加了Tencent的一面。原本安排了我上午去的,然而昨晚Tencent在bbs发了几轮不同职位的面试名单后都没有我份,我就认定没有了,原来技术职位的面试名单后来才出。结果今天早上去上课,人人都问我“你不是去Tencent面试的吗”,我faint……

去到酒店跟HR说明了,然后就照样填表,等面试官把这一时段内的面试完了就轮到我了。因为我冲忙赶去,没有打印他们指定的51job.com简历,直接拿了我的简历去参加面试了(其实这应该比51job.com的更清晰)。所谓的技术面,其实就是考综合素质吧,反正我不觉得考了什么技术。面试官拿着我的简历,问了我上面一些东西,例如MVP是什么啊,Imagine Cup是什么啊。然后他看到我熟悉Web开发,又随便问了一些相关问题,顺便考了一下IQ之类的。

我出来的时候,还是不明白到底我面试了什么职位,这是最faint的。到底他们招服务器端的人,是指Web服务器还是指IM服务器?另外我还看到有前端职位面试者的,那个是Web前端还是客户端软件的前端?反正我觉得我和面试官是对不上的,他说会推荐我去Web相关部门,那么难道Web相关部门没有出来做招聘?还真不知道呢……