我曾经听过一个老师对一个做的不错的程序员的一句评论:“某某某做程序确实很快,但就是都不能用”。事实上,直到今日,我们中很多人仍然在写这样的程序。不能用的程序一般都有几个特征,比如当程序出现问题时,总是不知道该怎么处理,甚至是在正常情况下都会出现问题,但归根结底就是压根就没有认真从用户的角度来考虑用户到底怎么来使用。如果用户输入了程序接受的输入,那么必然该给出正确的结果,可悲剧的是,就是有那么多的意外,因为客户行为总是在很多程序员意料之外,因此,当用户执行了某个程序员认为是非常规的操作的话,运行结果可能是用户无法得到任何问题的解释,然后用户下定一个结论就是:“这是一个什么破软件,真没用!”。
举个例子,民生网银就是一个巨大的悲剧,我上网银想要做一个操作,就是查询我的信用卡本期该还多少钱,但是我始终找不到那个菜单,原因在于:1 菜单字体太小,找起来真费劲;2 前几项看起来像是查询账户的,但点击后,悲剧的结果是提示“该卡为非储蓄卡”,我就纳闷那你为什么不提示我信用卡该做那个操作呢。于是我就找了客服问她如何查询,并向她抱怨网银字体太小,无用菜单太多,于是客服MM告诉我可以自己设置菜单的现实。可是,我哪知道该显示/隐藏那些一级菜单呢?最后我费了很多力气总算找到菜单,然后点击查询后,竟然要让我输入日期,我就寻思,为什么不直接显示最近一期的呢?这么麻烦我干么呢?最后越想越气,因为:1 这个操作过程非常不爽;2 做网银软件的这帮人真是坏了我们程序员的名声,怎么搞出这么难用的软件呢?要是逼这帮程序员天天用民生网银软件,估计也让他们折寿不少; 3 民生银行好歹算不错的公司了,怎么会上这么难用的软件呢?于是,我就很自然的、“邪恶”的联想到“特色”的社会主义。后来,我觉得我做为一个社会主义合法公民,我该“为中国的软件事业做点贡献”,给民生银行提点建议,于是我就想注册民生论坛发表一下意见。不幸的是,悲剧又来了,需要填写的注册信息真多啊,但更悲剧的是,在我填写完所有详细信息后,IE 9 下无法注册,总提示验证码不对。还好,我是程序员,我机器上有3个浏览器,于是,换了Chrome,注册了,但修改帖子不成功;淡定,我还有Firefox,结果出现了乱码。于是,我再次发挥程序员的职业素质,清理了Cookie,用注册好的账户,换回Chrome来发帖子,告诉民生网银有哪些易用性问题。经过一个小时痛苦折腾,算是OK了。要不是为了出心里的这口气,我才不会半夜1点钟忙到2点钟给民生银行反应问题呢(需要说明一下,客户MM态度很好,另外,半夜还要伺候我这样的人,还真难为她了。在她给我道歉时,我就回复她,这不是你的问题,然后转到论坛抱怨来了)。在我印象中,我活到现在半夜发抱怨的帖子仅有2次,一次是在电影院看了“要拍给下一代人看的”田壮壮导演亲自执导的《狼灾记》电影,另一次就是使用了民生网银。你也可以看到,程序员不替用户考虑最终会给用户带来多大的麻烦,也可想而知,如果这事发生在作为程序员的你身上或者你所在公司身上,会给你带来多大的信誉损失。
这类的悲剧也曾出现在我们团队身上,比如,我们的SaaS应用商店开放平台的应用让用户试用了应用之后,用户觉得不错后要继续使用,当用户购买以后却发现怎么也无法激活应用了。这对用户和我们来说,绝对是一个巨大的打击,先不说用户有多么恼火,就问你,你怎么给用户解释出现的问题呢。你总不能告诉用户,我们的程序员忽略了一个情况,“当应用过期时,应用不能激活了,因为要防止用户重复使用试用的应用”。这个问题让我思索了很久,因为我不能单纯的去怪罪我的程序员,因为毕竟在设计许可证授权这个模块时,由于我忙于别的任务,我没有去努力把好关,没有让他严格走我们的“概念设计->功能规范->设计规范->实现->QA”标准软件产品生产路线,但更重要的是,我没有把他们培养的和我一样,无论做什么东西,都必须以“用户场景驱动”来设计产品。因此,他们很容易忽视软件在非常规操作下的处理,以及一些很细节的问题。最后这件事情是在我用1个小时的时间给他解决好问题后,客户才最终释怀并表示理解和谢意。
我这篇文章想以2个例子来解释程序需要注意的细节和非常规操作下的软件处理。关于“用户场景驱动”设计,我在本文不做详述,大家可以查看《Framework Design GuideLine》这本书。
第一个实例:我们给一个客户提供尤埃开放服务平台(UIOSP) OEM版的激活工具。这个工具在正常运行下的界面如下:
(1)运行初始界面。
(2)输入序列号后,正在激活。
(3)激活成功,显示一些信息。
在这个小程序里面,需要考虑一下的小细节:
(1)界面不能被Resize,因为一旦Resize,将会变得非常丑陋;
(2)背景图片不能随着分辨率的变更而出现一些空白或者遮挡,这也非常的丑陋;
(3)可以支持“ESC”或者“Enter”按钮,从而避免用户使用鼠标操作;
(4)支持使用“Alt + <Key>”的组合键,这样方便用户使用键盘操作;
(5)当用户点击“激活”按钮时,要将所有的按钮变成Disabled,避免用户重复点击;
(6)激活过程要避免界面假死,就是当此时点击界面,界面能及时刷新;
(7)激活是要显示激活进程,提示用户,现在正在操作;
(8)界面的字体,要让用户能看清楚;
(9)异常处理:A)如果网络不畅通,用提示用户检查网络;B)如果许可证输入为空,提示用户输入;C)如果许可证无效,提示用户输入正确许可证;D)如果许可证已经超过使用限制,提示用户购买更多授权;E)如果激活成功,但不是使用管理员用户,则许可证在Win7下可能无法正确保存,那么必须提示用户设置权限或者使用管理员身份。
(10)如果用户点击取消或者关闭,要提示用户是否确定取消。
您或许还需要考虑国际化与本地化支持,此外还需要考虑是否遵循Section 508规范来支持更广泛的易用性规范,以及其它问题。不过,一个真正合格的软件需要考虑的细节确实非常的多,我也没有完全做到正确。下面,我用一个示例来介绍一下关于异常处理的实例。
第二个实例:许可证辅助工具,这是一个控制台程序,支持对SaaS应用商店开放平台应用许可证的操作。目前它提供了“-help”、“-alterlic”、“-alterconstraint”、“-listconstraint”、“-showmac”、“-listlicattr”和“-showlic”命令,分别用于获取帮助信息、变更许可属性、变更许可约束、列出可用的许可约束、查看本地Mac、列出许可证属性和查看一个许可证的信息。默认的是,让你将一个许可证拖拽到这个程序,自动显示该许可证的信息。这个程序采用了“管道-过滤器”体系结构来设计,我仅以“-alterlic”命令来展示如何进行异常情况处理。
2 {
3 class Program
4 {
5 static void Main( string[] args)
6 {
7 CmdLinePipeLine pipeLine = new CmdLinePipeLine(); // Pipeline-Filter Architecture。
8 var showLicense = new ShowLicenseDetailsFilter();
9 pipeLine.RegisterFilter( new AlterLicenseAttributesFilter());
10 pipeLine.RegisterFilter( new AlterConstraintFilter());
11 pipeLine.RegisterFilter( new ListConstraintsFilter());
12 pipeLine.RegisterFilter( new ShowMacFilter());
13 pipeLine.RegisterFilter( new ListLicenseAttributesFilter());
14 pipeLine.RegisterFilter(showLicense);
15 if (args.Length == 1 &&
16 File.Exists(args[ 0]) /* &&
17 Path.GetExtension(args[0]).ToLower().Equals(".lic") */)
18 {
19 try
20 {
21 showLicense.ShowLicenseDetails(args[ 0], string.Empty);
22 }
23 catch (Exception ex) // There is details exception in ex.Message. Thus, just show it directly.
24 {
25 System.Console.WriteLine(ex.Message);
26 }
27 System.Console.WriteLine();
28 System.Console.Write( " Press any key to exit. ");
29 System.Console.Read();
30 }
31 else
32 {
33 pipeLine.Handle(args);
34 }
35 }
36 }
37 }
该命令的格式为“-alterlic <File or Directory> [-deep true/false] [-lictype fx/bundle] <attribute>=<value>”。
用户输入异常可能为:
(1)File or Directory没有指定;
(2)File or Directory不存在,必须告诉用户输入正确的文件/文件夹;
(3)File不可读取,必须告诉用户更改权限;
(4)File格式不正确,必须告诉用户程序无法处理;
(5)如果是Directory,则是遍历所有的License File,那么,如果其中一个LicenseFile处理失败,则不能影响其它LicenseFile,但必须记录失败的License;
(6)如果用户输入的属性设置“<attribute>=<value>”是一个“aaa”格式的字符串,则必须提示用户,并且忽略掉;
(7)如果用户输入的属性设置“<attribute>=<value>”中attribute不存在,则必须提示用户,并且抛出异常,终止变更;
(8)如果用户输入的属性设置“<attribute>=<value>”中value与目标类型不匹配,则必须提示用户,并且抛出异常,终止更新;
(9)对许可证变更之前必须备份,以做回滚操作。
(10)其它细节或者异常处理。
在这里面,我们需要考虑的细节问题和异常处理问题比较多,我无法一一细述,仅供参考,希望能给大家一些思考。关于异常,我想强调一下,异常处理就是你需要提前考虑用户的非常规操作,并对用户的这类操作给出错误提示,并告诉用户如何去纠正。不过,我想,你也应该发现了,做一个在正常情况下能够工作的软件和一个在任何情况下都能工作很好的软件是有非常大的区别的,后则需要我们付出更多的努力和更专业的技能。
本文转自道法自然博客园博客,原文链接:http://www.cnblogs.com/baihmpgy/archive/2011/09/27/2192437.html,如需转载请自行联系原作者