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相关部门没有出来做招聘?还真不知道呢……

2007年10月30日星期二

固定订户与临时订户

从我对FeedBurner订户信息的跟踪看来,我觉得订户可以分为两类,一类是固定订户,一类是临时订户。

使用web reader订阅的,或者使用专用的client reader订阅的,属于固定订户,这些订户数量通常很少减少。另外email订阅的也能算这一类。原因是,用户一旦订阅了,就没什么理由去退订,除非你信息爆炸,flooding到用户受不了了,用户就会退订。反而你发布的频率不高倒的话,用户不一定能察觉到,他们可能已经忘记自己订阅此feed了。总而言之,固定订户的数量是几乎不会减少的,你只要能吸引用户成为固定订户,那么那个数字就是solid的,那就是你的一项real estate(不动产)。

至于另一类用户,则正好相反,它们使用浏览器自带订阅功能,或者是Outlook 2007等非专用client reader。这些订阅量绝对不是solid的,随着用户改变他们的软件使用习惯,订阅也就自然而然的被“取消”了。然而因为这些用户在一段时期内还算是活跃的,因此FeedBurner还是会把他们算上,但你在真正估计自己的feed价值时,应该把这层表面的泡沫给去掉。

最后,因为我的feed订阅数不算大,希望有相当订阅数的作者出来讲讲事实是否如此,例如aw或者猫影

在 C# 中 ("x" == "X") 何时成立?

这个问题初看起来很奇怪,C#就是C#啊,一门严谨的语言,并且字符串是区分大小写的,无论是在什么情况下都有("x" != "X"),这才叫做一致性嘛。事实上,这在以前一直都是成立的,直到.NET Framework 3.5引入了Linq to Sql,这种一致性就被破坏掉了,变成依赖于环境配置了。

想象一下我们对一个Linq to Sql的DataObject编写一个Linq查询,并且where子句包括("x" == "X"),那么该子句会返回true还是false呢?事实上,该查询虽然是一个用C#编写的lamda表达式,然而并不编译为MSIL。Linq to Sql里面的Linq是直接编译为SQL语句的,因此("x" == "X")会直接变成SQL里面的("x" = "X")。那么这就为true了?也不对,因为大小写是否敏感是基于数据库配置的,在当前的应用程序连接上特定的数据库之前,这个问题的答案都是不确定的。

那么我们可否选择使用String.Compare()来强制设置是否大小写敏感?这在Linq to Object中没问题,在Linq to Sql中就不行了,因为String.Compare()无法编译为SQL语句。因此,在Linq to Sql中,大小写是否敏感是一个依赖于环境配置的,这就提高了编码过程中由于疏忽而造成问题的概率。

为什么这样说呢?在以前,我们的C#代码和SQL代码是分开书写的,写C#的时候就很明确大小写敏感,写SQL的时候就很明确是数据库相关的。然而现在部分的SQL逻辑改为用C#来编写了,问题就出现了,特别是当你的代码中还混杂有Linq to Object的查询时,编写代码与阅读代码的过程中你一看到Linq就先要去想这段lamda表达式最终会被编译为哪种语言,MSIL还是SQL。如果你不进行这个区分,或者开小差把Linq to Sql的代码当作Linq to Object了,这就可能导致你编写了错误的代码,或者阅读上造成了错误的理解。

总体而言,虽然Linq to Sql为开发(特别是RAD)带来了巨大的便捷性,然而这种C#与SQL混合编写并且都使用C#语法的功能将会是一种先天的不足,它所带来的代码维护成本可能随着项目体积逐步增大而慢慢体现出来。至于将SQL混入C#所造成的分层模糊现象,我将在以后的文章中讨论,敬请关注:

Recruiting Events: Tencent (Part 1)

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

我去参加了Tencent的技术笔试,发现无论你报什么技术职位,最后都考一样的内容,可能先根据基础能力筛选一下吧。因为题目受NDA保护,那就不说细节了,只能说学习好操作系统(特别是UNIX)、计算机网络、数据库、算法与数据结构这几门课程是非常有必要的。你需要对课本里那些死记硬背的知识有所掌握,例如各种排序算法的实质。我通常就只记得几种常用的排序算法,那些哪方面都没有明显优势的排序算法就忘记了,事实表明这些东西最好定期复习一下。另外你还必须对课程内容的实际应用有所了解,例如SSH是OSI模型中第几层的加密,UNIX中的pipe如何创建,等等。

Tencent的笔试中那些程序填空题,原来的程序显然不是高质量的、通过编译的代码,感觉就是手写一段代码然后去掉几句让你填空。首先是有陷阱,例如:
for (#expression; #expression; #expression);
  if (#expression) #blank
    else #blank;
看到这样的缩进,不要以为if...else...语句在循环之内,因为for后面已经有分号了,这样缩进写法分明是为了给你视觉错觉的。另外还有一题算法主函数没有return的,然而main()就调用该函数并打印返回值,这显然是代码没经实际测试就投入试题了。

反正考这样的题目,我的命中率又不行了。其实我很怀疑这些企业到底招什么人,是不是招一个Web的前端或后端开发人员,平时写写JavaScript或者PHP什么的,紧急时候要求你能够马上抽调去写C++或者做UNIX维护。

2007年10月28日星期日

S.T.A.L.K.E.R

最近沉迷于《S.T.A.L.K.E.R - 切尔诺贝利的阴影》这个游戏,名字上那几个字母是Scavengers, Trespassers, Adventurers, Loners, Killers, Explorers and Robbers的缩写。从游戏类型上来划分的话,这是个FPS,不过因为和NPC的可交互性很强,也可以当一个RPG(反正Fallout3也换成第一人称视觉了),至于其背景设定也有点点恐怖游戏的成份。

其实我以前对恐怖游戏没什么兴趣的,因为玩起来不爽,整体而言是游戏玩你的心理感觉,而不是你玩它。开修改器当然可以爽一会儿,但如果那是个单纯的恐怖游戏的话,这样子玩一会儿又很没意思了。不过我现在对非纯粹的恐怖游戏有些感觉了,其实就是让你在一定的心理压力下完成同样的竞争性任务,本质还是和游戏安排好的规则竞争。例如Manhunt吧,它刚刚发布的时候我觉得这么压抑一个游戏没什么好玩,不就是杀人嘛,还不能群殴,要想办法一个一个引诱出来杀,这实在太无聊了。不过自从浪费了不少时间在Hitman2与Hitman4中尝试各种古怪组合玩法后,我也开始向hardcore级玩家靠拢了,开始追求那些“有难度”的玩法。

这其实很符合The Game里面所说的,人类这种奇怪的动物,只要给他们一个有等级划分的游戏,并且告诉他们你花越多的时间在上面,你就能进入越高的等级,他们就会花时间在上面争取高等级。有很多玩家都是从悠闲型开始的,但一旦他们中了这个法则,就开始慢慢转入hardcore玩家的行列了。

另外值得一提的是S.T.A.L.K.E.R那个名为A-Life的全局AI系统。根据官方的介绍,由于A-Life的存在,游戏不会再像以前那些基于脚本的RPG那样,仅当玩家做了某事才会触发某个变化,而更像是Simcity那样即使你什么也不做后台的AI模拟过程仍然会继续发生,这包括军团之间的互相攻击以及地区的重新划分,还有异种的成批迁移,或者是袭击军团。游戏里面有1000个个性各异的NPC,由于整个大背景是在AI控制下模拟的,因此你碰到这些NPC的过程会是相对随机的。然而我还没第一次完成游戏然后开始第二次,因此我并不确认这个随机性以及模拟真实度有多大,不过从平时的游戏过程中看来,确实NPC门都是好像真有自己的目的而行动的,并非就永远存在于游戏中的特定位置等待你的到来并交给你一个任务。

这种全局的开放性交互从来都是游戏设计师的梦想,没有人喜欢在一个脚本制订好的游戏里玩两次,因此一部分设计师希望通过MMORPG达成梦想,而另外一部分继续在单机游戏的AI上钻研。MMORPG是完全开放的,但可控程度也不高,游戏规则只能先影响玩家群体,再间接影响到对单个玩家的直接游戏体验,而单机游戏的AI是可以直接控制的,游戏体验是可以通过调整AI而直接调整的。将来到底哪一个能够带来更好的游戏体验,暂时还很难说,不过至少我还是倾向于选择单机游戏。

2007年10月27日星期六

副业

什么是副业?在这里我们探讨两种副业。

第一种,也就是现在所谓的Web前端开发,也就是编写符合语义的XHTML,加上具备浏览器兼容性的CSS,然后还有负责AJAX通讯以及DOM操作的JavaScript。在以前是没有前端开发这个说法的,因为这就是副业活,负责前端开发的,其主业通常是服务器端开发,又或者是图像设计,然后就兼职写写上述三种代码。正因如此,前端开发相关的这三种技术一直发展缓慢,因为人人都当它是副业,没必要也没时间去深究。例如JavaScript,其实它很早就支持我们现在所使用的Prototype中的各种功能,为什么当初就没有人去开发Prototype呢?首要问题就是没人觉得有必要去挖掘这些技术,就算有少数人挖掘到那个深度了,共享出来的知识其他人也不会去吸收——谁会了为副业而研读一大堆相关资料呢?因此,直到最近AJAX的热门,才导致了更多的人把前端开发当作主业来看待,也才突然有了那么多相关的技术讨论与书籍出版。

第二种,是中国学生所谓的研究与探索能力。有人说,中国学生的动手能力并不比美国学生差,其实这估计是基于双方都尽自己能力去做的前提下得到的机轮。不比别人差,并不代表着有同等的动机以及动力去做。就如前面说JavaScript是副业的例子,中国学生的真正缺失在于把动手研究与探索当作副业,是在应付考试这一主业得以游刃自如应付后才考虑的事情,同时并不会投入大量的精力关注有关的信息以及细节。

深入的就不探讨了,不过仅仅做一个小提示——你是否有留意到你生活中的某一样副业其实是极具潜力的,如果你不是很讨厌做那件事情的话,不妨考虑一下更换工作主题的时候将它提升上来作为主业,或许因为竞争对手少反而更容易成功。

2007年10月21日星期日

China MVP Open Day

在两日三天的海南三亚MVP峰会之后,终于回到了广州。MVP峰会的最大好处就是能见到来自各地的MVP,并且是面对面地交流,这真的比隔着一个Windows Live Messenger要好很多。另外三亚虽然不算是个一流享受的地方,不过看起来也还算很美。

我和另外两位广东的MVP,也就是彭斌黎波,都是在18号飞往三亚的,因为飞机1135起飞,所以我们约定了到了三亚进酒店之后再吃午餐。到了酒店之后,发现原来北京的MVP也到了,并且已经在餐厅吃饭了,就加入他们了。吃晚饭休息了一会儿,我们就到游泳池泡水兼聊天,我基本上就是跟着彭斌周围去认识别的MVP,不知不觉就在水里泡了n个钟。

晚餐前我跟着彭斌去找葛涵涛,发觉他们房间里的Windows Mobile设备还真多啊。之后俞晖也来了,拿着一个iPhone,我终于有机会亲眼看看iPhone那个屏幕了,并且体验了一下操作起来的手感,更加下决心要买个iPod touch。晚餐还是跟着北京的MVP,这次是去酒店外面的大排档吃海鲜,发现北京的MVP真是团结,总是一起行动。晚餐的海鲜感觉一般般啦,反正我不觉得哪一样做得特别鲜味的。晚餐过程中比较享受的是黎波的歌声,当然还包括现场高涨的fans情绪,哈哈。另外俞晖的iPhone在葛涵涛带领的Windows Mobile MVP群体攻击下,成为了“不具备企业应用能力”的反面例子。

晚餐回来后,还是泡水,哈哈……然后是跟彭斌、俞晖一起聊天,说到了博客园的发展,然后又说到了博客园最近上线的digg功能,另外还有一些各地.NET俱乐部发展的事情。我觉得这样的面对面聊天比在网上打字要轻松多了,虽然在线也可以音频会议,但要比划什么还是不如面对面的方便。我们聊天聊到一点多才睡,第二天要起来参加颁奖啊,睡眠不足啊……

19号早上是集体会议,包括Karen-Anne Young(APAC与EMEA地区Group Manager)的keynote,以及随后一些受NDA保护的内容,那些就不方便说了。在keynote之后,Karen亲自为我们每一位参加峰会的MVP颁奖,并且合影留念。19号下午是分组会议,Dev、IW和IT分开,我当然是去听Dev那场啦,内容包括Silverlight、Linq以及Windows Embedded。

晚餐是在游泳池边举行的自助餐,算是个小party吧,其中包括Karen的dancing哦!然后是问答环节,考察大家对微软与会员工的熟悉程度。接下来各位员工还各自出来自我介绍了一下,顺便还介绍了一下他们当前负责的社区项目。晚餐之后还是聊天时间,这次我和彭斌找到了陈荣陈黎夫,变成了ASP.NET MVP小聚了,可惜就缺了赵劼。回到房间后,已经十一点多了,王兴明过来吹水,又吹到了一点半。在这种聚会上面,你是不会愿意浪费时间在睡觉上的,因为有那么多有技术又能说的人在你身边,你总是想不停的找人聊天。

20号早上是沙滩活动,首先80多人一起在海滩上排一个MVP字样,让摄影师帮我们拍照,然后是小组比赛。之后有人踢足球,有人打排球,我就跟着彭斌去拣贝壳。拣贝壳这事情还挺考耐心与敏捷的,海水一冲上来你就要盯住有没有值得你捡的东西,有的话就要马上按住,否则水一退就又冲走了。顺着沙滩走过去,发现有不少外国人,哎……这里看来成美元区了,人家都在这里花美元,物价那么高,花人民币就感觉心痛。

下午我们一群MVP自发组织去吴支洲岛,逛了一个圈回来,其实也没什么特别的吧,就是不停的拍照拍照。这个自发组织啊,当然就不如微软出钱会务公司安排好那么体贴,有时候安排挺混乱的,有些人想这样又有些人想那样,团队管理问题啊……大多数MVP在工作中还真烦这个问题呢。从岛上回来后,我们直接进市区,吃了一顿麦当劳,买了一点特产,就打的去机场的。领票之后,我还傻傻的问11号门在哪里进,对方告诉我先进安检,我才想起这不是白云机场,不管你进几号门,都是一个安检通道。原本2115的飞机,延迟到2235,结果回到家已经凌晨了。

整次旅行的照片我会陆续挑选一些好看的发到facebook上面去,有兴趣的可以去看看哦。大家如果觉得有话题在三亚没讨论完的,可以在Messenger上继续讨论。

2007年10月15日星期一

Recruiting Events: Xunlei (Part 2)

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

前端工程师在广州的笔试复试只剩下4个人,我不明白为什么4个人都还要再笔试,如果前端技术的基础算是考察了,那么应该可以面试考一下设计思路以及综合能力,难道还要再笔试一次考更深入的技术吗?如果是那样的话,我觉得没有谁能做出来,因为即使第一次笔试那5道题,我问了猫影aw,他们也无法在不查询外部资料的情况下做出来(不过aw应该对JavaScript和ActionScript互调用那题很有体会,所以那题的答案应该找他讨论)。

发卷的时候,考官对我们这4个人说了一句“今天考一考基础”,然后看到试卷类别上写着C++,我就不知道说什么好了。C++我会读也会写,只不过基于我个人对C++语言本质的不太喜欢,所以也就不会think in C++,因此写出来的代码显然很不像C++ native speaker,并且会带上其他各种语言的一些accent。看看试卷,50道单选,20道填空,1道编程,有人要考基础我就跟他拼基础吧,只能这样了。

开头有几道选择题挺开放的,当也可能是纯粹考你最基础的基础。例如问题是以下哪个你看起来更顺眼,有一组答案中有两个分别是:
#define your_name "xunlei"
#define YOURNAME "xunlei"
这算是考什么呢?生硬的考他认为正确的命名规则呢,还是仅仅想了解你个人的编码风格呢?后面开始有一些题目考你对C++各种不一定人人用得着的功能,例如以下两个是否有性能差别:
#include<iostream>
#include "iostream.h"
这个……简直就犹如当年算法竞赛能够在初赛考你Pascal里面哪一个编译控制指令能改变堆栈大小一样,结果就是只有两类人能够做得出来:超级熟练的使用者,或者死记硬背的应试者。

填空题的话,也是有点像竞赛的笔试题,例如叫你看程序写运行结果,又或者补充程序代码中的空缺。ACM都直接上机考啦,C++还以这样的形式考察,不要说招前端工程师,就算真的是招C++开发人员,考察结果与实际能力只见也会有较大的误差吧。

最后的编程题,要求写一个字定义的strcpy函数,签名如下:
int strcpy(char* dest, char* source, int count)
其中count控制最大的复制字节数,返回值为实际复制了的字节数。题目不算难,就照我自己的思路写咯。后来Jeffrey Zhao才告诉我,这不正是strncpy吗?可惜strncpy的返回值不同,所以还是不能直接调用。

我还是100分钟左右就做晚了,然后又花了20分钟慢慢检查了一遍,修改了一些选项,其实改来改去都是不确认哪个是对的。之后等了一个晚上都没有面试通知,就知道出事了……

回过头来分析一下,到底什么人比较容易通过这样的两轮笔试呢?要熟记正则表达式,知道document.cookie存储格式,知道C++编译器的控制选项,这些都要求是超级熟练的使用者,或者死记硬背的应试者。我想没有任何企业想要后者,那么只能说迅雷想招前者,看重的是你是否是一个熟练的IT民工(来自于猫影词汇),是否上任后能马上投入工作。但如果这真的就是迅雷的招聘风格的话,我只能说和它招聘网站上宣传的相差太远的。网站上的宣传,让它看起来好像一家小百度那样,拥有大企业应该有福利,至少达到百度那个级别(当然宣传往往是比真是要夸张一些的,我说的百度级别是真实级别),同时又作为新兴企业拥有无限前景——这主要是指股票那事儿。然而事实上,可能它仅仅是有一家很中国式的小企业,就是作为整个大的世界工厂里的一个小工厂,依赖自身的光环招一些有理想的工人进去,然后……谁知道将来发生什么事情呢,这可是说不定的。

2007年10月13日星期六

Recruiting Events: Xunlei (Part 1)

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

第一次参加企业招聘笔试,竟然是迅雷……嗯……其实也没什么好“竟然”的,应该“竟然”的是“第一次”,完全没经验的情况下就跑去一所“陌生”的学校和一群陌生的人考试,而且课室里大部分人还不是和我考一样的题目。

我仅仅应聘一个职位,就是前端开发工程师,因此找到对应的课室就坐下来。之前幸运的碰到一位同班同学,他说简历投了三个职位,分别有短信通知他到三个不同的课室笔试,不知道他后来怎么办了。前端开发工程师的题目就5道大题,统一的答题纸上钩选应聘职位的竟然没有“前端开发”一栏,faint死了……

第1题考正则表达式,一个小题要求匹配字符串中首尾的空格,另一个小题要求匹配仅包含数字、字母、下划线的6到20位的字符串。我不熟悉正则表达式,所有东西都仅仅有个概念但记不住,想了很久也没写什么,最后决定凭直接写。第一题想不到空格是斜杠什么,空白字符好像是blank吧,于是写了个(\b)*.*?(\b)*上去,其实空白字符是space,应该是(\s)*.*?(\s)*,汗……中间那个.*?是非贪婪匹配,否则把末尾的空白自己也吃掉了,我只记得非贪来匹配好像是*后面加一个?,然后就写上去了。第二题我同样不知道[A-Za-z0-9_]其实就是\w,于是就写了[A-Za-z0-9_]{6,20}。

第2题要求写一个String类的扩展函数,要求按老ASCII码单字节/汉字双字节模式,计算一个字符串的字节长度。这个……我又载晕掉了,JavaScript中的String能够好像传统语言的char[]那样使用索引号获取单个字符,但是JavaScript没有带表单个字符的数据类型,获取到的还是字符串,两个字符串之间能够用大于号小于号比较的吗?在JavaScript中我还真的没这样用过呢,不过某些高级语言支持字符串的大小比较,结果相当于按字典排序后两个字符串的顺序比较,估计JavaScript也支持,所以我就按照那样子写了。答案如下:
String.prototype.byteLenght = function() {
  var count = 0;
  for (var i = 0; i < this.length; i++) {
    if (this[i] <= "\u00ff") { /*"\u00ff"是单字节ASCII码中最大的一个*/
      count = count + 1;
    } else {
      count = count + 2;
    }
  }
  return count;
}
在做完整份题目后,我又无无聊聊在代码后再加了一段注释,大意是:在JavaScript中实现这样的计算完全没有实际用途。首先,因为JavaScript是完全基于unicode的,按ASCII方式计算字节长度没有意义。其次,如果这时用于计算屏幕显示宽度的,其实也不准确,在非等宽字体中"w"和"i"的宽度差了几倍,显示宽度不能用这样的方法计算。

第3题是要求列举所知道的JavaScript与Flash中的ActionScript互调用方法,还要求提供代码说明。我仅仅在过去有过一点点了解,就是Flash的ActiveX对象对外暴露两个方法,分别用于传入和传出一个字符串,而这两个方法对应调用内部的ActionScript函数,因此ActionScript的处理应该写在这两个函数之中。当时就这样写上去了,并且说明我不记得具体的函数名称,给我查资料或者上网Google的话就会知道。我不知道阅卷那个人看到我每题都一堆回答以外的评论会怎么想,哈哈……后来查了一下,我印象中的那个函数其实是远古的fscommand,而Adobe现在建议大家使用ExternalInterface。

第4题要求列举cookie,并且包括过期时间。同样是不懂,记忆中只有document.cookie一样东西,从来没调用过,仅仅阅读过一次别人的调用代码,按照什么格式把相关属性保存在String我也不清楚,就知道第一个是键值对,至于过期日期……真的不知道了。既然连按照什么格式读取都不知道,所以这题我干脆就什么都没写,本来想着不会编写代码也能写写我所知道的,后来决定跳过。其实知道格式就很简单了,格式可以参考quirksmode上讲述cookie的文章

第5题是开放式的,要说自己擅长的前端开发方向。我用了一版纸回答前面的3个问题,又用了一版纸完成了第5题的吹水,哈哈……说了擅长XHTML + CSS + JavaScript,讲了一下过往项目,最后不忘插上一句“更多项目信息请浏览CatChen.biz”。

两个小时的笔试时间,我一百分钟做完,没有一道题是完全懂并且有把握的,因为我是习惯有什么不懂就去搜索的人,从来不背,而考到的点都是我平常很少用的。考完半天后就收到笔试复试通知了,接着第二天去复试。

2007年10月10日星期三

不要动态修改 DOM 里面的 id 属性

我不知道是否有什么标准规定不允许修改id属性,或者不建议修改id属性,总之IE对此支持不佳。在IE当中,修改id属性对将来的DOM操作没什么不符合标准的影响,你仍然可以通过document.getElementById()找到该元素,然后在CSS支持上就有问题了,id修改后并不会自动去重新匹配CSS规则,因此id修改后不会体现出应有的样式。

因此,进行DOM编程时应该避免修改id属性,如果需要动态改变匹配的CSS规则,就改变class属性吧。我碰到这个问题时也就是尝试通过修改id属性修改唯一匹配某个CSS过滤器的元素,结果发现修改后在IE中完全没有修改,之后改为用class属性实现同样的目的。

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的效率真的很高。