软件开发生命周期早期的功能点计算方法

by Daniel 24. December 2009 14:08

早段时间看到一篇Early Function Point Counting,觉得挺好就将他翻译过来共享给大家。

有需要的同事可以向我索要我整理的英文或中文版的Word版本.

NESMA (译者注:世界第二大功能点组织)承认的计算功能点的方法有如下三种:

ü  详细(Detailed)功能点计算方法

ü  估计(Estimated)功能点计算方法

ü  抽象(Indicative)功能点技术方法

“估计(Estimated)功能点计算方法”和“抽象(Indicative)功能点技术方法”由NESAM开发的,适合于应用在软件开发生命周期早期的功能点计算方法。NESMA“抽象(Indicative)功能点技术方法”世界著名,且被命名为“荷兰人的方法”。

本文讨论这几种功能点计算方法的适用性,研究结果和每种方法的精确度。

详细(Detailed)功能点计算方法

详细(Detailed)功能点计算方法就是我们所熟知的功能点计算方法(译者注:由IBM开发工程师首先开发出来,且从1986年开始由国际功能点用户组织IFPUG维护和提供培训和认证。培训要6k/人), 执行过程如下:

ü  决定功能点类别 (类别有:ILF, EIF, EI, EO, EQ)

ü  确定每个功能的复杂度 (复杂度分为:Low, Average, High)(译者注:这个过程比较复杂,繁琐,耗时)

ü  计算总功能点数量

估计(Estimated)功能点计算方法

估计(Estimated)功能点计算方法执行过程如下:

ü  决定功能点类别 (类别有:ILF, EIF, EI, EO, EQ)

ü  确定数据类型功能(ILF, EIF)的复杂度为Low, 交互类型功能(EI, EO, EQ)的复杂度为Average

ü  计算中的功能点数量

很明显,“详细(Detailed)功能点计算方法”与“估计(Estimated)功能点计算方法”的区别是:后者省去了确定每个功能点的复杂度的复杂繁琐过程,而是将数据类型功能的复杂度默认为Low,交互类型功能(EI, EO, EQ)的复杂度默认为Average

抽象(Indicative)功能点计算方法

抽象(Indicative)功能点计算方法的执行过程如下:

ü  决定数据类型的功能点(ILFs and EIFs)数量

ü  利用下公式计算总功能点数量

抽象(Indicative)功能点数量= 35 x ILFs + 15 x EIFs

这个估计只是基于当前的逻辑文件 (ILFs and EIFs)

抽象(Indicative)功能点技术方法基于:假设每个ILF有三个EIs(如增、删、改)2EOs(2个报表)1EQ(如查询功能); 每个EIF1EO和一个EQ.(译者注:只有满足上条件用参数3515计算出来的结果的精度才高,否则要参考前面两种方法的复杂度与功能点数量关系适当调整这两个参数)

三种计算方法示例

这节通过一个简单的小案例来说明这三种功能点计算方法。刚开始用户只说个一句话的需求:我们的系统需要维护客户在产品数据,且要查看到供应商数据。

如果你需要越精确的功能点计算结果,则需要越详细的用户需求。我们是按照用户需求由简到详细的过程来展现三种不同的功能点计算方法,也更好的体现三者的区别。

抽象功能点(Indicative)计算方法

用户需求:

ü  系统需要维护客户在产品数据,且要查看到供应商数据。

这个粗糙的需求已经足够进行我们的抽象(Indicative)功能点计算:

ü  ILF: 客户、产品

ü  EIF: 供应商

数据类型的功能

功能点类型

功能点(用计算公式中的默认值)

客户

ILF

35

产品

ILF

35

供应

EIF

15

抽象(Indicative)功能点总数

85

估计(Estimated)功能点计算方法

要执行估计(Estimated)功能点计算,我们必须知道交互功能,所以更具体的用户需求是必须的:

用户需求:

ü  用户要求系统能增加,修改,删除客户数据;需要查询客户数据;并且要4个不同的具有一定计算的客户报表

ü  用户要求系统能增加,修改,删除产品数据;需要查询产品数据;并且要1个具有一定计算的产品报表

ü  用户要求系统能查询供应商数据;并且需要1个有汇总功能的供应商报表

这个相当详细的用户需求显示了真实的交互功能点的数量,所以我们能够进行估计(Estimated)功能点计算。

数据或交互功能点

功能点类型

复杂度

(默认值)

功能点数量(确定的历史数据)

客户

ILF

Low

7

产品

ILF

Low

7

供应商

EIF

Low

5

客户Add

EI

Average

4

客户Change

EI

Average

4

客户Delete

EI

Average

4

客户查询

EQ

Average

4

客户报表 1

EO

Average

5

客户报表2

EO

Average

5

客户报表3

EO

Average

5

客户报表4

EO

Average

5

产品Add

EI

Average

4

产品Change

EI

Average

4

产品Delete

EI

Average

4

产品查询

EQ

Average

4

产品报表

EO

Average

5

供应商查询

EQ

Average

4

供应商报表

EO

Average

5

估计(Estimated)功能点总数

85

详细(Detailed)功能点计算方法

要执行详细(Detailed)功能点计算,我们不仅要各种类别(EI, EO, EQ, ILF, EIF)的功能点数量,我们还必须确定每个功能点的复杂度 (Low, Average, High)

FPA(Function Point Analysis)中,决定功能点的复杂度是基于DETsRETs的数量和被引用的功能数量。

(译者注:一般EI, EO, EQ的复杂度决定于被更新和引用的功能和DET数量,而ILFEIF的复杂度决定于DETRET的数量)

下面就是为什么用户需求需要分析得更加的详细:哪些数据元素(DETs)和逻辑文件(文件类型被引用的,也就是ILF, EIF )被交互功能使用(EI, EO, EQ),那些逻辑数据组(也叫记录元素) (RETs)和数据元素 (DETs)被数据类型的功能(ILF, EIF)包含。

下面是详细的分析用户需求得出的功能点数量结果:

数据或交互功能点

功能点类型

复杂度

(默认值)

功能点数量(确定的历史数据)

客户

ILF

Average

10

产品

ILF

Low

7

供应商

EIF

Low

5

客户Add

EI

High

6

客户Change

EI

Average

4

客户Delete

EI

Low

3

客户查询

EQ

Low

3

客户报表1

EO

Low

4

客户报表2

EO

Average

5

客户报表3

EO

Low

4

客户报表4

EO

High

7

产品Add

EI

Average

4

产品Change

EI

Low

3

产品Delete

EI

Low

3

产品查询

EQ

Average

4

产品报表

EO

Average

5

供应商查询

EQ

Low

3

供应商报表

EO

Average

5

 详细(Detailed)功能点总数

85

结论

在这个特别的案例中,所有三种方法的结果都是85个功能点。通常的,这些结果不会严格一样的,但是都会相当接近的。接着,在这篇文章后面你将看到估计(Estimated )功能点计算和抽象(Indicative)功能点计算方法精确度的研究结果。

什么时候使该用哪一种功能点计算方法

详细(Detailed)功能点方法计算的数字当然要比估计(Estimated)功能点计算方法计算的数字或抽象(Indicative)功能点计算方法计算 的数字要精确得多。但是详细(Detailed)功能点方法也将要花费更多的时间,需要更详细的需求。具体是使用哪种功能点计算方法取决于我们的项目经理和我们所处在的系统生命周期阶段。

在很多系统中,我们应用抽象(Indicative)功能点计算方法得出了今人惊讶的好的估计。经常的,比较起来使用抽象(Indicative)功能点计算方法更容易,因为数据模型(译者注:数据类型的功能点,也就是ILF,ELF)是可得到的,且可以毫不费力的得到。

基于 100+ 个项目的研究结果

NESMA使用大约100个已开发且实施的系统做了一些关于估计和抽象功能点计算方法精确度的研究。NESMA对这些实施的系统同时严格的利用三种不同的计算方法进行了计算。结构是下面展示的两张图:

ü  估计(Estimated )功能点计算方法得出的规模与详细(Detailed )功能点计算方法得出的规模对比

ü  抽象(Indicative)功能点计算方法得出的规模与详细(Detailed )功能点计算方法得出的规模对比

我们看见一个很好的相互关系(直线)在两个图片中。然而,我们在 抽象(Indicative)计算中,我们看到相当大的偏离(多达大约50%) 在一些案例中。这就是为什么我们要小心使用抽象(Indicative)计算方法的原因。这种方法的优点是我们可以在很短的时间内很容易的得到一个粗糙的系统规模估计。

在一个系统中拥有更多的 (或者更少) 的输出,我们可能需要去修改乘数3515;但是计算的方法和公式我们仍然可以使用。

估计(Estimated )功能点计算方法和详细(Detailed) 功能点计算方法的结果则十分接近。

 

 

 

Tags:

Joel Spolsky的软件开发成功12法则之二

by Daniel 15. December 2009 09:05

接上一篇Joel Spolsky的软件开发成功12法则之一, 具体介绍Joel 12衡量法则的细节.

1. 你们用不用源代码管理工具(Do you use source control)?

我用过一些商业化的源代码管理工具,我也用过免费的工具,比如CVS,告诉你吧,CVS挺好用。但如果你根本就没有用源代码管理工具,那你就是累死了也没法让你的程序员出活:他们没法知道别人在改动什么源文件,写错了的源文件也没法恢复。

使用源代码管理工具还有一大好处是,由于每一位程序员都是把源代码从源代码管理工具签出到程序员本地硬盘,几乎不会发生丢失源代码的事,最起码我还没听说过。

2. 你们可以把整个系统一步构建吗(Can you make a build in one step)?

这句话问的问题是:从你们最新的源码开始到建立起能够交出去的最后文件,你们有多少步骤要做? 一个好的团队应该有一个批处理程序一步便可将所有的工作做完,像把源文件提取出来,跟据不同的语言版本要求(英文版,中文版),和各种编译开关(#ifdef)进行编译,联接成可执行文件,标上版本号,打包成安装文件或直接送到网站上去,等等等等。

如果这些步骤不是一步做完,就有可能出人为差错。而且当你很接近产品开发尾声的时侯,你可能很急于把最后几个虫解决,然后尽快地交活。如果这时候你需要做20步才能把最终文件制出来,你肯定会急得要命,然后犯一些很不该犯的错误。

正因为这个原因,我工作的前一个公司从用WISE改用InstallShield:我们必需要让我们的批处理程序完全自动化地,在夜里,被NT scheduler起动把最终文件制成,WISE不能被NT scheduler启动而InstallShield可以,我们只能把WISE扔掉。(WISE的那帮家伙向我保证他们的下一代产品一定支持在夜里自动运行)

3. 你们有每日构建吗?且每天白天都有构建系统至少一遍吗(Do you make daily builds)?

你们有没有遇到过这样的事情:一个程序员不小心把有毛病的源码放进源代码管理系统,结果造成构建失败。比如,他建立了一个新源文件但忘了把它放进源代码管理工具,然后他高高兴兴锁机回家了,因为在他的机器上整个编译得很好。可是别人却因为这没法工作下去了,也只好闷闷地回家了。

这种造成最终构建没法完成的情况很糟糕,但却很常见。如果每天在白天就构建一遍的话,就可以让这种事不造成太大危害。在一个大的团队里,要想保证有毛病的源码及时得到纠正,最好每天下午(比如午餐时)构建一次。午餐前,每个人都尽可能地把改动的源文件签入到源代码管理系统里,午餐后,大家回来,如果构建成功,好!这时大家再从源代码管理系统里取出最新的源文件接着干活。如果构建出错,出错者马上修正,而别人还可接着用原有的没问题的源程序干活。

在我以前曾干过的微软Excel开发组里,我们有一条规定:谁造成构建出错,谁就得被罚去负责监视以后的构建过程,直到下一位造成构建出错的人来接任他。这样做不仅可以督促大家少造成构建出错,而且可以让每个人都有机会去了解构建过程。

如果想更多了解这个话题,可以读Joel的另一篇文章 Daily Builds are Your Friend.

4. 你们有软件Bug管理系统吗(Do you have a bug database)?

不论你有任何借口,只要你写程序,哪怕只是一个人的小组,如果你没有一个系统化的管理软件虫的工具,你写的程序的质量一定高不了。许多程序员觉得自己可以记得自己的软件虫。没门!我从来记不住超过2到3个软件虫。而且第二天早上起床后忙着去买这买那,好不容易记住的软件虫早忘掉了。你绝对需要一个系统来管住你的那些虫。

软件虫管理系统功能有多有少。但最少要管理以下几种信息:
如何重复软件虫的详细步骤(如何重现问题)
正常情况(无虫)应是怎样(本来应该是怎样)
现在情况(有虫)又是怎样(可是你的是怎样)
谁来负责杀虫(负责人)
问题有没有解决(状态)

如果你觉得用软件虫管理系统太麻烦,可以简化一下,建立一个有以上5列的表来用就行了。

如果想更多了解这个话题,可以读Joel的另一篇文章Painless Bug Tracking.

5. 你们在写新程序之前总是把现有程序里已知的软件Bug解决吗(Do you fix bugs before writing new code)?

微软Windows Word的第一版的开发项目曾被认为是“死亡之旅”项目。好象永远也做不完,永远超时。所有人疯狂地工作,可怎么也完成不了任务。整个项目一拖再拖,大家都觉得压力大得受不了。最后终于做完了这个鬼项目,微软把全组送到墨西哥的Cancun去度假,让大家坐下来好好想想。

大家意识到由于项目经理过于强求程序员们按时交活,结果大家只能匆匆地赶活,写出的程序毛病百出。由于项目经理的开发计划并没有考虑杀虫的时间,大家只能把杀虫的任务往后推,结果虫越积越多。有一个程序员负责写计算字体高度的程序,为了图快,居然写一行”return 12;”了事。他指望以后的测试人员发现这段程序有毛病后报告他再改正。项目经理的开发计划事实上已变成一个列写程序功能的清单,而上面列的所谓程序功能迟早都会成为软件虫。在项目总结会上,我们称这种工作方法为“绝对劣质之路”。

为了避免再犯这个错误,微软制定了“零缺陷策略”。许多程序员嘲笑这个策略,觉得经理们似乎在指望靠行政命令来提高产品质量。而事实上“零缺陷策略”的真正含义是:在任何时候,都要把解决现有程序里的问题作为首要问题来抓,然后再去写新程序。

为什么要这样做呢?

一般说来,你越不及时地杀虫,杀虫的代价(时间和金钱)就会越高。比如,你写程序时打错了一个字,编译器马上告诉你,你很容易就把它改正。你刚写好的程序在第一次运行时发现了一个问题,你也很快就能解决它,因为你对你刚写的程序还记忆犹新。如果你运行你的程序时发现了一个问题,可这个程序是几天以前写的,你可能就需要折腾一会儿,还好,你还大致记得,所以不会花太长时间。但如果你在你几个月以前写的程序里发现了问题,就比较难解决了,因为你已经忘了许多细节。这时候,你还没准儿正忙着杀别人程序里的虫呐,因为这家伙到加勒比海阿鲁巴岛度假去了。这时候,解决这一堆问题的难度不亚于从事尖端科学研究。你一定得小心翼翼地,非常系统化地从事,而且你很难知道多长时间你才能把问题解决。还有更糟糕的,你的程序已交到用户手里了,才发现问题,那你就等着掏腰包吧。

总结起来,就一条:越早解决问题,越容易解决。

另外还有一个原因,刚写的程序里发现问题,你能够比较容易地估算解决它的时间。举个例子,如果我问你写一段程序去把一个列表排序需要花多长时间,你可以给我一个比较确切的估计。如果你的程序,在Internet Explorer 5.5安装以后,工作不正常。我问你要多长时间把这个问题解决,你恐怕都估计不出来,因为你根本就不知道是什么原因造成了这个问题。你可能要花三天时间才能解决,也有可能只花两分钟。

这个例子告诉我们,如果你的开发过程中有许多虫没有及时解决,那你的开发计划肯定不可靠。反过来,如果你们已经把已知的虫全部解决了,要做的事只是写新的程序,那你的开发计划就会比较准确。

把已知的虫全部解决,这样做还有一个好处:你可以对竞争对手快速反击。有些人把这叫着“让开发中的产品随时处在可以交给用户的状态”。如果你的竞争对手推出一个新的功能想把你的客户抢走,你可以马上在你的产品里加上这个功能,立刻将新产品交付用户,因为你没有一大堆积累下来的问题要解决。

6. 你们的产品开发日程安排是否反映最新的开发进展情况(Do you have an up-to-date schedule)?

为什么我们需要开发日程安排?如果你的程序对公司的业务很重要,那公司就必须知道你的程序何时能写完。满世界的程序员都有一个通病,那就是他们都搞不清自己何时才能写完要写的程序。他们都只会对管理人员嚷嚷:“等我做好了就做好了!”

不幸的是,程序写完了,事远远没完。作为一个公司,在发行产品之前,还有许许多多的事情要做:何时做产品演示?何时参加展览会?何时发广告?等等。所有的这一切都依赖于产品的开发日程安排。

定下产品开发日程安排,还有一个很关键的好处:它逼着你只做叫你做的功能,甩掉那些可要可不要的功能,否则这些可要可不要的东西有可能把你缠住。

定下产品开发日程安排,按照它开发,这并不难做,请看Joel的另一篇文章 Evidence Based Scheduling ,这篇文章告诉你一种制订产品开发日程的好方法。

7. 你们有没有软件开发的详细说明书(Do you have a spec)?

写软件开发的详细说明书就像是绣花:人人皆知是好东西,可没谁愿意去做。

我不知道这是为什么,也许是因为多数程序员天生就不喜欢写文章。其结果是,一个开发组里的程序员们,宁可用程序来沟通,也不愿写文章来表达自己。他们喜欢上来就写程序,而不是写什么详细说明书。

在产品的前期设计过程中,如果你发现了一些问题,你可以轻易地在说明书里改几行字就行了。一旦进入了写程序的阶段,解决问题的代价就要高得多了,不仅仅是时间上的代价,而且也有感情上的代价,因为没人愿意将自己做成的东西扔掉。所以这时候解决问题总有一些阻力。

没有产品开发详细说明书就开始写程序,往往会导致程序写的乱七八糟,而且左拖右拖不能交付使用。我觉得这就是Netscape遇到的问题。前四个版本的程序越写越乱,以至管理人员作出一个愚蠢的决定:把以前的程序统统扔掉,重新写。后来他们在开发Mozilla时又犯了同样的错误。产品越做越乱,完全失控,花了几年的时间才进入内部测试阶段。

我的一贯主张是:让那些不情愿写文档的程序员们接受一些写文章的培训和训练,而以上所说的糟糕的例子就有可能少发生。

另一个解决问题的办法是:雇一些能干的项目经理,专职写产品开发详细说明书。

不论采用以上哪种方法,道理只有一个:在没有产品开发详细说明书之前,决不可写程序。

如果想更多了解这个话题,可以读Joel的4-part series

8.你们的程序员是否工作在安静的环境里(Do programmers have quiet working conditions)?

当你让你的智囊们工作在安静,宽敞,不受人打扰的环境里,他们往往能更快地出活,这已是不争的事实。有一本经典的讲软件开发管理的书Peopleware(人件) 把这个问题阐述得很清楚。

问题在于,我们都知道最好不要打断这些智囊们的思路,让他们一直处于他们的最佳状态中,这样他们就能全神贯注,废寝忘食地工作,充分发挥他们的作用。作家,程序员,科学家,甚至篮球运动员都有他们的最佳状态。

问题还在于,进入这个最佳状态不容易。我觉得平均起来,需要15分钟才能进入最佳状态,达到最高工作效率。有时侯,当你疲劳了或已经高效率地干了许多工作了,你就很难再进入这个状态,只好干点杂事打发时间,或上网,玩游戏等。

问题更在于,你很容易就被各种各样的事打扰,被拽出你的最佳状态:噪音啦,电话啦,吃午饭啦,喝杯咖啡啦,被同事打扰啦,等等。如果一个同事问你一个问题,只花你一分钟,可你却被拽出你的最佳工作状态,重新回到这个状态需要花半小时。你的工作效率因此而受到很大影响。如果让你在一个嘈杂的大房间里工作(那帮搞网站的家伙还就喜欢这样),边上的推销员在电话里大叫大嚷,你就很难出活,因为你进入不了你的最佳工作状态。

作为程序员,进入最佳工作状态更难。你先要把方方面面的细节装在脑子里, 任何一种干扰都可能让你忘掉其中某些东西。你重新回来工作时,发现好些东西记不起来了(如你刚用的局部变量名,或你刚才的搜索程序写到哪里了等),你只好看看刚写的程序,回忆一下,慢慢地回到你刚才的最佳工作状态。

我们来做一个简单的算数。假设一个程序员被打扰一下,哪怕只有一分钟,他却需要花15分钟才能回到最佳工作状态(统计资料显示如此)。我们有两个程序员:杰夫和愚夫, 坐在一个大办公区里工作。愚夫想不起来用什么函数去进行Unicode 字符串复制。他可以花30秒查一下,或者花15秒问杰夫。由于他坐在杰夫的旁边,他就选择去问杰夫。杰夫被打扰了一下,耽误了他15分钟,节省了愚夫15秒钟。

现在,我们把他们俩用墙和门隔开,让他们俩分坐在不同的办公室里,愚夫又想不起来什么函数名,自己查一下要花30秒;问杰夫,要花45秒,因为他要站起来走过去问(对这帮程序员来说,这可不是件简单的事,看看他们的体质就知道为什么了)。所以他选择自己去查。愚夫损失了30秒钟,可是杰夫少损失了15分钟。哈哈!

9. 你们是否使用现有市场上能买到的最好的工具(包括软件和硬件)(Do you use the best tools money can buy)?

如果你用于编译的时间超过几秒钟,你就应该换一台最新最快的计算机了。因为如果编译时间超过15秒,程序员们就会不耐烦,转而去上网看一些无关的东西比如The Onion,弄不好一看就是好几个小时。

调试图形界面软件时,用只有一个显示器的计算机不仅不方便,有时甚至是不可能。用有两个显示器的计算机,要方便许多。

程序员们经常不可避免地要去画一些图标或工具栏图。多数程序员没有一个好的图形编辑器可用。用微软的“画笔”软件去画图标简直是笑话,可事实上大家还就在这样做。

在我的前一个工作,系统管理员成天给我发来自动警告,说我在服务器上使用了超过220兆的空间。我告诉他,按现在硬盘的价钱,超出这点空间的价钱远低于我用的厕纸的价钱。让我花10分钟去清理我的文件绝对是我工作效率的莫大浪费。

一流的开发组绝不折腾它的程序员。工具落后会让人用起来觉得难受,一点点积累起来,会让程序员们成天叫苦,而一个成天叫苦的程序员绝对不会是一个高效率的程序员。工欲善其事,必先利其器

再添一句,要想使你的程序员高兴,最好的办法就是给他们买一些最新最棒的工具软件。用这种方法可以让他们乖乖地为你工作,这可比用高薪吸引他们来得便宜得多。

10. 你们有没有专职的软件测试人员(Do you have testers)?

如果你的开发组里没有专职的测试人员,或没有足够的测试人员(两到三个程序员就应该配一个测试员),那你的产品就一定是毛病百出。想在测试员身上省钱,绝对是打错了算盘。我真不明白为什么这么多人算不过来这笔帐。

Joel有另一篇文章专门讲这个,请看Top Five (Wrong) Reasons You Don't Have Testers

11. 你们招人面试时是否让写一段程序(Do new candidates write code during their interview)?

我问你,让你去招一个魔术师,你是否连看都不看一眼他的魔术玩得怎样就要他?当然不会!

你举办婚宴,要请一个厨师,你是不是连尝也不尝他做的菜好吃不好吃就要他?我想也不会。

奇怪的是,几乎每天都有这样的事发生:在面试一个程序员时,简历写得漂亮,谈得热火朝天,问几个简单的问题(如CreateDialog()和DialogBox()有什么区别?这种问题,查一下帮助文件就知道了),人就招进来了。你真正应该关心的不是这人记不记得这些写程序的边边角角的东西,而是他能否出产品!更糟糕的是,许多问题是知道就知道,不知道,想死也不知道的问题。

不能这样下去了!在面试时,请一定要让写一段程序。在Joel的这篇文章里Guerrilla Guide to Interviewing,我有许多好建议。

12. 你们是否有随便抓一些人来试用你们的软件(Do you do hallway usability testing)?

这句话的意思是,让你从走道里走过的人中,随便抓几个人来,让他们试用你的软件。如果你抓五个人来用你的软件,那你就可能把你的程序中95%的不方便使用的地方找出来。

要想让用户去买你的软件,你必须要设计好你的用户界面。这其实并不难。你可以读Joel的free online book on UI design打打基础。

用户界面设计的关键是,如果你让几个人去用你的软件(五六人可能就够了),你可能很快就找出最大的问题。想知道为什么吗,请读Jakob Nielsen的文章Why You Only Need to Test with 5 Users, 只要你坚持随便抓一些人来试用你的软件,你就能将你的用户界面设计得越来越好。

Tags:

General

Joel Spolsky的软件开发成功12法则之一

by Daniel 15. December 2009 09:03

作者简介:
作者:Joel Spolsky 是纽约市一家小软件公司Fog Creek Software 的创始人。他毕业于耶鲁大学,曾在美国微软公司、Viacom、Juno 任软件设计师及经理。他的网络日志“Joel谈软件”(Joel on Software)非常有名,读者人数可以排进全世界前100名。

这篇文章是Joel写于Auguest 09, 2000, 原文地址是The Joel Test: 12 Steps to Better Code, 虽然是9年前写的文章,但对于我们还是有参考意义。

“有没有听说过SEMA?这可是衡量一个软件开发组好坏的很深奥的系统。别动,等一下!别按那个链接!给你六年你也搞不清这玩意儿。所以我自己随便攒了一套衡量系统,信不信由你,这系统,三分钟就可掌握。你可以把省下的时间去读医学院了(译注:美国的医学院可是要读死人的!)。

(注:SEMA:Software Engineering Measurement and Analysis)

Joel 衡量法则:

怕翻译不标准,造成理解困难,附上鸟文原版

  1. 你们用不用源代码管理工具?
      Do you use source control?
  2. 你们可以把整个系统一步构建吗? 
      Can you make a build in one step?
  3. 你们有每日构建吗?且每天白天都有构建系统至少一遍吗?
      Do you make daily builds?
  4. 你们有软件Bug管理系统吗?
      Do you have a bug database?
  5. 你们在写新程序之前总是把现有程序里已知的软件Bug解决吗?
      Do you fix bugs before writing new code?
  6. 你们的产品开发日程安排是否反映最新的开发进展情况?
      Do you have an up-to-date schedule?
  7. 你们有没有软件开发的详细说明书?
      Do you have a spec?
  8. 你们的程序员是否工作在安静的环境里?
      Do programmers have quiet working conditions?
  9. 你们是否使用现有市场上能买到的最好的工具(包括软件和硬件)?
      Do you use the best tools money can buy?
10. 你们有没有专职的软件测试人员?
      Do you have testers?
11. 你们招人面试时是否让写一段程序?
      Do new candidates write code during their interview?
12. 你们是否随便抓一些人来试用你们的软件?
      Do you do hallway usability testing?

“Joel 衡量法则”好就好在你只需照着逐条回答以上问题,然后把所答为”是”的问题算成一分,再加起来就可以了,而不需要去算什么每天写的程序行数或程序虫的平均数等等。但咱丑话说在前面,可别用”Joel 衡量法则”去推算你的核电站管理程序是否可靠。

如果你们得了12分,那是最好,得了11分还过得去,但如果只得了10分或低于10分,你们可能就有很严重的问题了。严酷的现实是:大多数的软件开发公司只能得到2到3分。这些公司如果得不到急救可就玄了,因为像微软这样的公司从来就没有低过12分。

当然,一个公司成功与否不仅仅只取决于以上标准。比如,让一个管理绝佳的软件公司去开发一个没有人要的软件,那开发出来的软件也只能是没有人要。或反过来,一帮软件痞子以上标准一条也达不到,没准照样也能搞出一个改变世界的伟大软件。但我告诉你,如果不考虑别的因素,你只要能达到以上12条准则,你的团队就是一个可以准时交活的纪律严明的好团队。”

The Joel Test 软件开发成功12法则的四个实用领域:

1. 用该法则来衡量你的软件开发组,告诉我你得的分数,让我们来品头论足O(∩_∩)O~。

2. 如果你是开发组的经理,用该法则来使你的组提高效率。如果你们一上来就能得12分,你就别再打扰你的程序员了,专心致志别让公司的管理人员来烦你的程序员吧。

3. 如果你在找一份程序员工作,问问你未来的老板他能得几分,如果分数很低,你一定要确信你进去后有足够的权力来改变这一切,否则,最好躲远点,不然,你在那儿会很难受的。

4. 如果你是投资者,正在决定是否向一个软件公司投资,或者你的软件公司正在决定是否兼并另一个软件公司,该法则可以帮你做决定。

 

 

Tags:

General

Common Library 之 ResourceProvider 多语言实现

by Daniel 19. August 2009 13:33

这个功能相信大家都早已知道。

1. 在在web.config定义特定的culture和uiCulture, 如果应用自定义resource prodiver的话, 加上resourceProviderFactoryType的设置:

如:

<globalization culture="en-us" uiCulture="en-us" resourceProviderFactoryType="MTRC.IRMIS.Common.ResourceProvider.DBResourceProviderFactory, IRMIS.Common "/>

culture和uiCulture也可以设置成auto, 这样就会取浏览器首选区域设定, MTRC.IRMIS.Common.ResourceProvider.DBResourceProviderFactory为provider factory类的命名空间加上类名, IRMIS.Common 则为dll的名字。

2. 运行中更改语言类别。对浏览器首选语言区域的检测是在页面什么周期早期, 可以在basepage基类中overirde InitializeCulture, 并且修改Thread.CurrentThread.CurrentUICulture和Thread.CurrentThread.CurrentCulture。 如:

 

protected override void InitializeCulture()

    {

        string language = string.Empty;

        if (Session["lang"] != null)

        {

            language = Session["lang"].ToString();

        }

        if (!String.IsNullOrEmpty(language) && (language != "Auto"))

        {

            Thread.CurrentThread.CurrentUICulture = new CultureInfo(language);

            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(language);

        }

    }

3. 可以在后台也可以在页面上直接调用资源

- 后台方法调用: GetLocalResourceObject 或 GetGlobalResourceObject, 不多说, 相信大家都知道

- 直接在页面有两种方法: 隐式和显式, 下面简单说说他们俩的区别。

a. 调用方法不同(废话, 真是的)

显示表达式:

调用全局资源 <asp:Literal id="Literal1" Text="<%$ Resources:riskCommon, ltrRiskStatus %>" runat="server"></asp:Literal>, 其中riskCommon是resource type字段, ltrRiskStatus是resource key

调用本地资源<asp:Literal ID="ltrUserName" runat="server" Text="<%$ Resources:ltrUserName %>"></asp:Literal>

隐式表达式:

调用本地资源<asp:Localize runat="server" Text="Deafult value" meta:resourcekey="lblProviderType"></asp:Localize>

b. 隐式表达式只可以调用本地资源

c. 隐式表达式存储值方式不一样, 在database里面如lblProviderType.text是Text属性的resource key。当然也可以配置其他属性, 如Visible的resource key 是lblProviderType.Visible

d. 隐式表达式是通过resourcereader去取资源的, 也就是说会一次把该resource type下的所有资源一次拿出来, 更有利于性能。而显式表达式是一条一条取的。(通过验证, 隐式表达式还是会一个一个跑一次)

e. 隐式表达式通过resource provider取不到值时会用默认值, 如上例中的Text="Default value", 而显式表达式会有编译错误(有点烦)。

注意:如果只是为了显示值,请使用控件<asp:Localize .., 上面有例子。

请大家指出不同意见和补充不全的。

 

Tags:

Common Library

Common Library - Demo project 布局

by Daniel 17. August 2009 16:59

要求:

1. 跨浏览器

2. 代码简单,样式结构分离

3.使用div+css布局,自适应高度, 有min-heigh, min-width

最后效果如下(menu css和部分图片来源于towngas,有些同事会有似曾相识的感觉): 

 

masterpage 代码如下: 

 

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Main.master.cs" Inherits="CommonLibrary.Demo.Module.Common.Main" %> 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title></title>

    <asp:ContentPlaceHolder ID="cphHead" runat="server">

    </asp:ContentPlaceHolder>

</head>

<body>

    <form id="form1" runat="server">

    <div id="main">

        <asp:ScriptManager ID="ScriptManager1" runat="server" />

        <div id="header">

            <div id="logo"><br/></div>

            <div id="sysName">VS.NET 2008 Common Library Demo Project</div>

            <div id="language">

                <ul>

                    <li>

                        <asp:LinkButton ID="lbtnEn" runat="server" CommandArgument="en-us" OnCommand="ltbnChgLang_Click">ENGLISH</asp:LinkButton>

                    </li>

                    <li>

                        <asp:LinkButton ID="lbtnZhCn" runat="server" CommandArgument="zh-cn" OnCommand="ltbnChgLang_Click">简体中文</asp:LinkButton>

                    </li>

                </ul>

            </div>

        </div>

        <div id="menu">          

            <asp:Menu ID="menuMain" runat="server" DataSourceID="smdsMain"></asp:Menu>

            <asp:sitemapdatasource runat="server" id="smdsMain" />

        </div>       

        <div id="nav">

            <div id="path"><asp:SiteMapPath runat="server" id="smpWeb" /></div>

            <div id="logonOut">

                <asp:ImageButton ID="ibtn" runat="server" ImageUrl="~/App_Themes/Default/img/frame/nav_exit.png" Text="Logon Out" OnClick="ibtnLogonOut_OnClick"></asp:ImageButton>

            </div>

            <div id="logonUser"><asp:Label ID="lblUserName" runat="server" Style="color: #0033CC;"></asp:Label></div>

        </div>

        <div id="content">       

            <asp:ContentPlaceHolder ID="cphContent" runat="server"></asp:ContentPlaceHolder>

        </div>

        <div id="footer">@copyright 2009 AiPuJing Software Technology Co. Ltd</div> 

    </div>

    </form>

</body>

</html>

 css 代码如下:

 

body { margin: 0; padding: 0; font-family: "Arial", "Helvetica", "sans-serif"; font-size: 12px;text-align:center; background: url(../img/frame/form_bg.gif) repeat-x; background-color:#EDEDED;}

 

div#main { height: auto; width:985px; margin:0 auto; padding-bottom:5px; text-align:left;background-color:#ffffff; }

 

div#header {height: 70px; background: url(../img/frame/header_bg.jpg) repeat-x;}

div#header div#logo{float: left;padding-left: 180px; background: url(../img/frame/header_logo.gif) no-repeat;}

div#header div#sysName { float: left; padding-top: 18px; font-family:"Times New Roman", "Times", "serif";font-size: 28px; font-weight:bold; color:#FFFFFF;}

div#header div#language { float: right; padding: 10px 15px 0 0; font-weight:bold;}

div#language ul li {list-style-type: none;}

div#language a:link,

div#language a:visited,

div#language a:focus,

div#language a:hover,

div#language a:active { color: #ccc; text-decoration: none;}

 

div#menu { background: url(../img/frame/menu_bg.gif) repeat-x; text-align: left; height: 30px;}

div#nav { height: 19px; padding:2px 0 0 15px; background: url(../img/frame/nav_bg.gif) repeat-x; /*background-color:#e6f0fa;*/}

 

div#path { float: left;}

div#logonUser { float:right; padding-left:18px;background: url(../img/frame/nav_user.png) no-repeat;}

div#logonOut { float:right; padding:0 10px 0 10px;}

 

div#content {height: auto !important; height:440px; min-height:440px; margin-top:1px; text-align:center; color:#004499;} 

div#footer { height:30px; line-height:28px;background:#3C7FAF; text-align:center; color:#ffffff;}

 

供参考和学习, 望大家提意见和真正


Tags:

Common Library

Common Library - Menu之数据库设计篇

by Daniel 6. August 2009 15:56

综合大家的讨论,晓冰在其博客Common Library 菜单控件提出了9点基本需求和实现方式, 另结合实际应用,我加上下面两点:

10. 支持一个User多个Role

11. 实现基于11.的Function级别的权限控制

下面是数据库设计图:

上述6个表说明如下:

 

1. tbl_ComLib_Menu 

该表在上次讨论中已经和大家见面了, 不多唠叨。需要注意的是:指定Active为false的时候,请确保其所有孩子都是false。

2. tbl_ComLib_User

很显然, 这是一个用户信息表,没什么需要说的。只列出了几个常用字段,大家可以扩展。

3. tbl_ComLib_UserRole

存储用户角色基本信息,有两个新鲜的字段:‘HomePage' 和’Priority'。‘HomePage'指的是用户角色在登录系统的时候的默认页面,由于我们实现了一个用户可以有多个Role,所以这里的’Prilrity‘就有用了, 指定那个Role优先级别高。比如登录的时候,进入优先级别高的Role的‘HomePage'.

4. tbl_ComLib_UserRoleUserAssignment

用户和Role的关联表。很简单,就两个字段,存储用户和Role的映射关系。

5. tbl_ComLib_UserRolePermissionFunction

 Role和Menu的关联表。(也就是MTR项目里常用到的tbl_RoleRight表, 只是换了个马甲而已)

需要说明的是:

1. 数据除了可以继续像之前MTR项目一样,所有的Menu item都必须关联到Role, 也就是说如果menu中有10条数据,有2role, 则该表有10*2 = 20条数据,如果有访问权限的话, Active设置为true, 否则设为false(这是之前MTR项目的做法). 如果要新加menu, 则该表也为每个role添加相应的menu。

2. 也可以只存储有访问权限的数据, 也就是说该表中只存储有访问权限的数据(当然Active字段也可以去掉了)。我想这种方法存储过程会复杂点, 要找出该记录的所有父亲节点(好的是SQL2005之后可以用WITH temptbl 递归找父亲)。

现在Demo用的是第二种方法,只给Role指定有权限访问的数据, 且只要指定叶子节点即可。 如果所有的Role都有访问权限的叶子节点则不需要在该表中配置记录,默认所有Role都是有访问权限的(用*表示). 也就是说如果该表中一条记录也没有, 默认所有Role对所有Menu都有访问权限。但是现在有一个问题:如果新加一个menu, 且没有配置任何权限在permission function表, 但是该menu的parent 配置了权限, 比如配置了SYS-ADMIN, 但是没有配置给GENERAL, 这个时候, 用GENERAL登陆, 则是给GENERAL添加parent menu的权限?还是不给呢?如果不给的话, 新加的menu会找不到parent而抛出异常。从功能上好像应该给GENERAL添加parent menu的权限, 但是从权限控制的角度, 也就是说用户信息保密的角度,应该不添加该parent menu.  大家认为该怎么处理呢?

6. tbl_ComLib_Resource

Common Library示例menu的title和description存储在resource file

如果有需要将Menu的多语言数据存储在数据库中,则可以存放于该表中, 可以通过简单修改存储过程实现, 其他多语言数据也可存储于该表,如label等, 然后用ResourceProvider事先多语言, 这是下一个Common Library工作项。实例数据如下:

不确定的地方,请留下您的宝贵意见:

1. 数据库表,字段命名规范

2. tbl_ComLib_UserRolePermissionFunction有提到两种数据方案,如果有其他更好的当然更好。

3. Menu的多语言数据是放在数据库还是resource file?

如果有不一样的想法希望您能留下宝贵想法和意见。这只是个初稿, 且有些想法也没有确定,Common Library的建设需要您的一份力量。

另提点意见: 我们的博客真的不太好用1. 太慢了, 图片加载要等。 2. upload图片很不爽, 要刷新页面,也要等。 3. 最不爽的是编辑不太好使。可能是我发帖太少,还没习惯吧。也或者大家都是先在word写好再paste到这里发布, 也或者用email发布, 所以感觉不到这些问题。

Tags:

Common Library

Report layout的问题

by Daniel 14. July 2009 15:22

有下面三个问题解决经验或方法的同事麻烦回帖或单独找我, 必谢!

1. 有报表需要Excel和PDF格式, 在PDF格式里面, 页码计算是正确的, 但是Excel里面的,打印的页码不正确, 总是显示1/1。

2. Excel 报表一个单元格内容没有完全展开, 见下图

3. 报表Risk Description 列对应到三个列, 不可以用来排序。见下图:

Tags:

问题征答

Web标准在中国

by Daniel 9. July 2009 13:10

 

在十月初的时候,我有幸在中国呆上了一段时间,与Web领域的专家、学生等交流Web标准以及他们的现状。很有意思的几个礼拜,也让我大开眼界。印象最深刻的,在中国推行Web标准的仍在少数,并且通常是孤立无援的,无法实施、无法去解释为何需要标准及标准的价值。所以这里我想写一下我所了解的情况、面对的挑战和一些希望能有效的方法。

如果你有任何意见建议,欢迎留言!如果你有相关内容链接,也欢迎提供,我会更新文末的资源列表。

市场力量

在中国,驱动Web标准的主要动力与欧洲国家、澳大利亚以及美国几乎是相反的。没有任何法律要求你的网站具备可访问性(Accessibility),整个市场也起不到什么推动作用。市场的推动很有意思。我经历过的那些商业项目,Web标准都是很重要的基础,即便是在有法律约束的国家。为什么说市场推动很有意思呢,因为网站的拥有者虽然清楚法律要求网站达到可访问性要求,但除非看到切实的利益,否则仅仅会只花最小的成本去满足法律上的要求。

目前在中国,Web标准因为一些原因在商业上比较脆弱。比如IE6仍然占据浏览器市场份额的95%。大部分依赖于ActiveX控件才能运行的电子商务网站使得人们必须用IE6。这就导致了在制作网页的时候趋向于满足IE6,而很少的关注其他浏览器。伴随着Opera、Safari、Firefox、Google Chrome使用率的上升,这种状况正在逐渐改善。实际上,Google Chrome的问世让Web设计届更加关注浏览器兼容性。开发者们也谈到了,虽然他们经常使用IE外的浏览器来开发和测试网站,但仍需要不时的使用IE,仅因为很多网站依赖于它。

对网站兼容性的低需求导致了开发者、公司都没什么动力。不过这应该会改善,尤其是越来越多的跨国公司外包或将开发工作放在中国。希望这种逐渐渗透能够生效,在中国的外国公司应该会对知识的提升有所帮助。我问过一位Microsoft的开发者是如何开始接触Web标准的,他说因为公司请了一些专家来培训Web标准的开发知识。这是相当好的,而且是推广Web标准发展的一个关键渠道。Opera一直是Web标准的拥护者(声明一下,我为Opera工作但此处并非借机推广),也一直在推广Web标准,在中国的也是核心的开发部分,开发团队也是非常活跃的参与着聚会、会议。

法律支持

因为没有具体的法律要求网站具备可访问性,我们来看看奥运会对此的影响,很有意思。因为比赛,公共区域、街道和建筑已经具有很好的适应性及无障碍措施,也让人更清醒的意识到这一点。最起码这是一个开始,且中国已与2008年7月批准了联合国《残疾人权利公约》。这是历史上第一个保障和促进残障人士权利的国际性法律公约。中国批准了此公约,意味着残障人士在获取信息、康复、就业和教育方面都有了法律依据。如公约第九条所说:

“缔约国应当采取适当措施…促使残疾人有机会使用新的信息和通信技术和系统,包括因特网。”

接下来还有很长的路要走,但起码中国已经加入了。

基层的拥护

最让人激动的是我从很多Web领域的专家身上看到的激情和责任感。在中国有一些有影响力的博客在推广着Web标准。比如JunChenRealazy。与Realazy沟通时他提到了2005年时候他第一次开始写关于标准的博客,每天能有接近1000的点击。这或许意味着人们渴望学习更多相关知识,即使他们并不是真正在搜寻这些信息,但他们访问到他的网站,看到了关于标准的介绍。

与我交流的几乎所有开发者都说他们基本上是自学的。在很多国家,Web开发和标准并非都存在于大学课程中,所以设计师、开发者必须自学。在中国,最大的学习障碍是缺乏资源。对大部分人来说一些昂贵的电子书也加剧了学习障碍。

我最激动的事就是参加了在中国的第一次Web Standards Café。在北京举办,由Opera赞助,主题是Web标准和Web 2.0,基本上讨论集中在在中国我们怎样支持Web标准。结合开发者和BarCamp这类聚会,我认为这是一条正确的道路。

支持Web标准,在中国

有些事我们已经可以开始做起,来促进Web标准和网站可访问性设计在中国的发展。可能看上去有些吓人,但从一点点做起,Web标准不可能不会变的更加流行。如同一句中国古话说的:“纸上得来终觉浅,绝知此事要躬行”。不久前我们在欧洲、澳洲和美国也还在努力为Web标准做一些基础工作,这些经验值得我们回过头去学习。目前我认为以下途径对Web标准在中国的发展是有利的:

翻译资源 - 首要的任务就是有中文的、免费的资源供大家使用、学习。当前很多个人已经投入到翻译中(见最后的资源部分),但我忍不住想那些大公司也应该贡献他们的力量。详见W3C资源翻译介绍和指南

跨国公司的责任 - 大的国际公司在国际范围内推广和支持Web标准,也应该尽他们所能帮助中国的Web标准发展。比如通过内部培训、赞助或者提供课程或者中文化Web标准资源。当然也不仅限于中国如此。

基层的拥护 - 开发者比任何人更了解面对的机遇和挑战。博客、论坛、BarCamp聚会、Web Standards Café等形式都是比较有效的途径。这可能会根据中国文化的不同采取不同的形式,但本质上一定是交流和分享。

如果你是一个博客、开发者或者企业内部的布道者,请一定分享你的经验。如同我上文所说,如果你有任何意见建议,欢迎留言。如果你有任何相关链接希望分享,我也会更新拓展文末的资源列表。

资源

其他

评论
千鸟 - 2008年11月25日 11:24

汗,“纸上得来终觉浅,绝知此事要躬行”都晓得。除了加倍努力,不得不梦想照进现实,对Producer来说,大局观最重要,陆游也还有句“汝果欲学诗,工夫在诗外。”

话又说回来,在web-based以用户为中心的设计过程中,通过标准化直接给用户带来的良好体验微乎其微。就是因为这些个种种客观原因,影响从业人员的实际操作经验,缺乏理论总结,从而导致水准停滞不前。如果都靠自学,那得花上N倍的时间和精力,国情行情人情似乎都有点理想化。

JunChen - 2008年11月25日 12:19

@千鸟 原文作者似乎学过中文;本来Web标准主要就是为开发者、为企业服务的,通过这样再给用户带来好处。

William - 2008年11月25日 13:47

国际友人对我国的WEB标准如此关注。感谢。
在先如今,不可能奢望立法的支持,
标准的执行的确要靠利益驱动。
也需要开发者对自己生产的代码有一定的追求,
让简洁、高效、标准化的代码不只存在理想状态下。

则名 - 2008年11月26日 09:44

会好的。去年比前年好,今年比去年好。还是要身为web开发者的我们不断推进和实施。

kejun - 2008年11月26日 10:32

无利不起早,在中国推广Web标准仅仅从理论基础层面布道,无疑举步维坚,只有在强调高质量前端开发的拉动下,在产品品质和开发效率上体现出价值是最好的推动。这方面国内的前端行业严重缺乏交流。每年的D2是仅有的纯技术应用层面的交流。但尚缺少大公司的赞助。

国内前端开发者目前尚缺乏探索性。正如所说"纸上得来终觉浅,绝知此事要躬行"啊,要实践啊,这是书里永远得不到的!希望D2能成为前端开发者实践经验分享的平台,前端技术创新分享的平台。顺便说一声11/29第三届D2将在上海举行,http://d2forum.org

行云流水泵 - 2008年11月26日 11:56

WEB标准,应该也是由目标导向驱动的

hax - 2008年11月26日 17:29

比起5年前W3北京会议几乎没有业界关注,现在的情形已经好很多了,已经有许多人在关注和努力了。

芋头 - 2008年11月26日 17:37

中国现在基本上都是按照出资者的需求开发的,很少根据访问者的需求开发WEB。
普及标准,还有一段很长的路……

Henny Swan - 2008年11月26日 17:39

Thanks Junchen for both the translation and republishing - it's much appreciated. I've read with interest (with the help of Google translate!) all your comments and would love to learn more. By all means email me if you'd like to talk further. My details can be found on my blog at www.iheni.com.

Cheers, Henny

睡到自然醒blog - 2008年12月04日 00:18

确实这样,我的wordpress主题就是在firefox下表现不好,但我自己也不太会修改代码 :(

coffee - 2009年05月30日 13:24

假如真的标准了,那些“野战军”就失去作用,要不转行,要不重新学标准,而那些该死的天文,是最大障碍。其实标准需要慢慢的推行,而学习又是一大的飞跃,现在从业者能干到什么时间都是个未知数,还谈得上学习吗?

coffee - 2009年05月30日 13:28

在国外是一个萝卜一个坑,而国内是一个人要管美工,要管策划,要管N件事,LB恨不得把你变成孙悟空有七十二变才成。


 

Tags:

Copyright © 2009 APJ Software

最新评论

Comment RSS

公告

欢迎使用APJ Blog!

日历

<<  February 2012  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

View posts in large calendar