工作变化

昨天办完了交接手续,正式从凤凰新媒体(www.ifeng.com)离职,如果一切顺利的话,我将在6月初加盟一个做SNS社区的创业团队。

从毕业入职到现在刚好满两年,要离开已经熟悉的人和熟悉的事情,有点感伤。在工作交接的两周时间,每天都恍恍惚惚,好像我是在做梦一样,怀疑自己是不是真的做出了离职的决定。每天都一如既往地按时上班,每天中午聚餐的时候也都和同事们海阔天空的聊天,每天晚上回家才发现距离最后的离职日期又少了一天。

在凤凰的两年,是我职业生涯的第一步。在这两年中我有很多的收获,技术能力和业务经验都有了一些积累,在人际交往方面也有很大的长进(这个方面最大的收获就是要经常站在别人的角度考虑问题)。部门在2006年和2007年都获得了优秀团队的奖励,这是我最值得骄傲的事情。昨天资讯中心的刘老师请我在魏公村的鑫百万吃饭,每人三瓶啤酒下肚之后,得知我在资讯中心的口碑还是比较高的,工作也得到了编辑们的肯定。凤凰所有共事的同学们,多谢了。

离职的原因很简单,我想有一个拼命学习的环境,把技术能再提高一些。那为什么一定要离开凤凰呢?其实我也不算是真的离开,加入凤凰的时候自己注定这辈子是个凤凰人,说不定只是一次暂别。最后祝凤凰新媒体的事业蒸蒸日上,更上一层楼,也希望自己在互联网行业能走得更远。

[ 分类: 动态 News ] 由 Pan 发表于 May 31, 2008 11:41 am  固定链接  评论( 6 )

有意思的lambda表达式

有这么一种场景,有一个Category的实体类对应着一个名为Category的数据库表。Category类中有Name和Path属性,在新建或更新Category对象时要保证这两个属性在数据库中的值唯一。当然你可以在数据库中的Name和Path字段都建一个唯一索引,那如果不采用这种做法应该怎么做呢?最简单的方法就是写两个名为CheckNameValid和CheckPathValid的函数。很明显这种做法不易于维护,比如说当Category类增加其他需要保证唯一的属性或者属性名字需要改变的时候。我的写法如下:

  1. /// <summary>
  2. /// 检查数据库字段中的值是否唯一
  3. /// </summary>
  4. /// <param name="t">Cateroy的集合</param>
  5. /// <param name="func">表达式</param>
  6. /// <param name="isNew">是否是新建Category</param>
  7. /// <returns></returns>
  8. protected bool CheckValid(
  9.                 IEnumerable<Category> t,
  10.                 Func<Category, Boolean> func,
  11.                 bool isNew)
  12. {
  13.     Category c = t.SingleOrDefault(func);
  14.     if (isNew)
  15.         return c == null;
  16.     else
  17.         return c == null || (c != null && c.ID == Convert.ToInt32(ID.Text));
  18. }
  19.  
  20. //调用方法一
  21. protected Func<Category, Boolean> func = null;
  22. protected void Page_Load(object sender, EventArgs e)
  23. {
  24.     func = selectCategory;
  25. }
  26. protected bool selectCategory(Category c)
  27. {
  28.     return c.Name == this.Name.Text;
  29. }
  30. CheckValid(kr.Categories, selectCategory, true);
  31. //调用方法二
  32. CheckValid(kr.Categories, delegate(Category c){ 
  33.         return c.Name == Name.Text;
  34.     }
  35.     , true);
  36. CheckValid(kr.Categories, delegate(Category c){ 
  37.         return c.Path == Path.Text;
  38.     }
  39.     , false);
  40. //调用方法三
  41. CheckValid(kr.Categories, c => c.Name == Name.Text, true);
  42. CheckValid(kr.Categories, c => c.Path == Path.Text, false);

三种调用方法,当然是使用了lambda表达式的方法三最简洁明了。还可以扩展一下,可以把它写成某个Utility类泛型静态方法,专门检查实体类在数据库字段中的值是否唯一,这时Convert.ToInt32(ID.Text)就要抽出来做成一个参数,这里不就写了。

另外在瘦子师兄的点拨下,发现C# 3.0里面有个类似Javascript中prototype的概念,称为扩展方法(Extension Methods),在System.Linq中运用了大量的扩展方法(例如前面例子中的SingleOrDefault)。扩展方法貌似没prototype灵活,比较的例子如下:

  1. public static class Program 
  2. {
  3.     public static void Main(string [] args)
  4.     {
  5.         string s = "Hello World";
  6.         s.Print();
  7.         s.ToString();
  8.         Console.WriteLine(s.ToString());
  9.         Console.Read();
  10.     }
  11. }
  12. public static class Tools
  13. {
  14.     public static void Print(this string s)
  15.     {
  16.         Console.WriteLine(s.ToUpper());
  17.     }
  18.     public static void ToString(this string s)
  19.     {
  20.         Console.WriteLine(s.ToUpper());
  21.     }
  22. }
  1. function A(){this.name = "AA";}
  2. A.prototype = {
  3.     show:function(){ alert(this.name);}
  4. }
  5. var a1 = new A();
  6. a1.show();
  7. //直接覆写A的prototype中的show
  8. A.prototype.show = function(){ alert("modified:"+this.name);}
  9. var a2 = new A();
  10. a2.name = "a2";
  11. a2.show();

对于一个你不能修改源代码的类,可以通过这种方式为它添加方法。但这个只是编译期的(s.Print()翻译成tools.Print(s)),也就是说编译之后是不能通过反射来取得它的,它并不是要扩展类(例子中的String类)中的真正方法,而是某个静态类(例子中的Tools)的静态方法。
具体介绍参见这篇文章Deep Dive on Extension Methods

[ 分类: 学习 Learning ] 由 Pan 发表于 May 25, 2008 4:28 pm  固定链接  评论( 0 )

静态列表页的分页

相比于动态网站的列表页分页,静态列表页的分页是件麻烦的事情。因为旧的列表页发布出去以后就无法知道之后发布的总页数,而且也不可能每次更新列表页时去更新所有以前发布过的列表页(频道树节点多时,这个量非常大),所以一般的CMS处理得都比较简单。比如说新浪很多频道的列表页都是按日期归档的,一天发表的文章生成一个列表页,而有些专题的子频道列表页直接就没有处理分页。凤凰网列表页分页就只有上一页和下一页的链接,系统在处理的时候就更新两个列表页-当前最新的列表页和最后更新过的列表页,规则是当上次处理分页后更新的文章数大于等于三分之五Pagesize时进行分页操作。搜狐列表页就要复杂一些,有列表页首页链接,有输入页码跳转等,在页尾的Javascript脚本中里面有一个变量maxPage,猜测应该是后期include包含进去的。

在最近的一个项目中,对列表页的分页处理比上面说的几种要复杂一些。不看代码,先看效果
这里的静态列表页是这样的,最早发布出去的第一页是列表页名字_1.html, 第二页是列表页名字_2.html,以此类推,到最后一页就是最新的一页是列表页名字.html或者列表页名字_列表页总页数.html。

下面是代码

  1. var Paging = {
  2.     holder:'paging',
  3.     currentNo:1,
  4.     currentFileName:'',//当前文件名,不包括后缀名和页码
  5.     listSize:10,//列表项链接的显示数目
  6.     totalNo:35,//列表页总页数
  7.     //初始化currentFileName和currentNo
  8.     preRender:function(){
  9.         var _lastIndex = location.href.lastIndexOf('/');
  10.         var _fileName = location.href.substr(_lastIndex+1);
  11.         var matchItem = _fileName.match(/(.*)_(\d+).html/i);
  12.         if(matchItem != null){
  13.             this.currentFileName = matchItem[1];
  14.             this.currentNo = parseInt(matchItem[2]);
  15.         } else {
  16.             this.currentFileName = _fileName.split('.')[0];
  17.             this.currentNo = this.totalNo;
  18.         }
  19.     },
  20.     render:function(){
  21.         //在页面上显示的页码
  22.         var realNo = this.totalNo - this.currentNo;
  23.         //页码对listSize的倍数
  24.         var tens = Math.floor(realNo / this.listSize);
  25.         //页码对listSize的余数
  26.         var units =  realNo % this.listSize;
  27.         var html = '';
  28.         html += '<span><a href="'+this.currentFileName+'.html">首页</a></span>';
  29.         //如果不是在第一个listSize,例如第一个十页,就显示上一页
  30.         if(tens != 0) {
  31.             html += '<span>'
  32.                 +'<a href="'+this.currentFileName+'_'+(this.currentNo+1)
  33.                 +'.html">上一页</a></span>';
  34.         }
  35.        
  36.         var _no=0,_linkNo=0,_isCurrent=false;
  37.         for(var i = 1 ; i < this.listSize+1 ; i++){
  38.             _no = tens*this.listSize+i;
  39.             //链接为倒序
  40.             _linkNo = this.totalNo - _no + 1;
  41.             _isCurrent = i == units+1 ? true : false;
  42.  
  43.             //到达最后一页就退出
  44.             if(_linkNo < 1) break;
  45.             //是否当前页
  46.             if(_isCurrent)
  47.                 html += '<span class="current">';
  48.             else
  49.                 html += '<span>';
  50.             //是否是列表页第一页
  51.             if(_no != 1 && !_isCurrent)
  52.                 html += '<a href="'+this.currentFileName+'_'+_linkNo.toString()+'.html">';
  53.             else if (!_isCurrent)
  54.                 html += '<a href="'+this.currentFileName+'.html">';
  55.             html += _no.toString();
  56.             html += '</a>';
  57.             html += '</span>';
  58.         }
  59.        
  60.         //如果没到最后一个listSize,显示下一页
  61.         if(tens != Math.floor(this.totalNo / this.listSize)) {
  62.             html += '<span><a href="'+this.currentFileName+'_'
  63.                 +(this.currentNo-1)+'.html">下一页</a></span>';
  64.         }
  65.         html += '<span><a href="'+this.currentFileName+'_1.html">末页</a></span>';
  66.         $(this.holder).innerHTML = html;
  67.     },
  68.     init:function(){
  69.         this.preRender();
  70.         this.render();
  71.     }
  72. }
  73. Paging.init();

其中代码中的Paging.totalNo是列表页的总数,这个是后期加入的。目前可以想到两种方法给脚本传递这个值,一个就是像搜狐一样在页面中include进去一个后期更新的文件,另外还有比较别扭的方法,就是最后更新的列表页传入一个值,形如doclist.html?535,后续打开的页面都会带着这个值,如果万一不是从第一页进入的比如从搜索引擎过来的话,在页面中会记录一个该列表当时发布的总页数作为补充。最后还是决定使用搜狐的方法,在更新最新的列表页时同时新发一个记录列表页总数的文件。另外为了SEO,可以专门做一个汇总归档页,当然也可以由CMS生成上一页和下一页的链接,这些方法都是容易做到的。

脚本浏览地址:http://panweizeng.com/others/itv_playlist_paging/paging.js

[ 分类: 学习 Learning ] 由 Pan 发表于 May 24, 2008 10:12 pm  固定链接  评论( 0 )

默哀三分钟

为表达全国各族人民对四川汶川大地震遇难同胞的深切哀悼,国务院决定,2008年5月19日至21日为全国哀悼日。今天(5月19日)14时28分,公司给每人发了一朵白玫瑰,全体起立默哀三分钟。

[ 分类: 动态 News ] 由 Pan 发表于 May 19, 2008 10:16 pm  固定链接  评论( 0 )

立春是部好电影

尽管看的是阉割版,但依然能看出这是一部极为优秀的作品,其中蒋雯丽的演技可圈可点。看到梦想去巴黎唱歌剧而现实生活潦倒的王彩铃(蒋雯丽饰演),我马上想到了另一位同样热爱歌剧的有理想青年Paul Potts。对于理想青年来说,王彩铃带给我们的是幻灭,Paul带来的则是希望。关于立春的介绍和精彩点评,可以到豆瓣上面看看。

让Paul再感动我们一把!

[ 分类: 动态 News ] 由 Pan 发表于 May 18, 2008 6:37 pm  固定链接  评论( 0 )