qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

软测自动化之矛与盾

  相比于之前的全手工测试,现在的测试无疑自动化的多,回首看颇有封建社会过渡到资本主义社会之感。已经“自动化”了好几个月了,一直想总结总结这种由鸟枪更换成的大炮到底给我们测试带来多少生产力的提高,它适用什么场景,它对于测试的最终目的有多少帮助,又会带来多大的影响?

  其实说起来听可笑的,我对于自动化测试起初还是挺抵触的,总觉得自动化了之后会有一些很“隐藏”的缺陷会被放掉,而且针对小作坊式的软件生产,不需要对每个软件模块都进行全方位测试。往往将前后端一集成,发布一个包,部署给环境,测试就好了,没有那么多的要求。可是当软件进入批量和大规模之后,各个模块之间的接口通信就变得异常重要,每个环节首先必须保证自己是没有问题的才能集成进环境,还有软件开发都是这样,先后台再前端,往往到了项目后期,才集成UI联调,此时后端的功能和接口都必须已经测试过保证没有大问题的情况下进行的;再者软件后台的程序都属于项目前期开发,更多的不确定和更多的缺陷等着测试人员去发掘。在这种情况下手工测试的缺点就暴露出来,一是不能及时提供有效的页面给予手工测试,二是不停的代码变更让手工测试已经很难满足复用的需要,这时候的自动化就犹显重要。

  啰嗦了一堆,其实就是想说自动化在软件进入规模化生产之后测试人员的必经之路,可以大大的提升测试效率和节省测试时间,让这个软件过程在最短的时间内完成。

  前面提到了自动化测试适用的测试过程,现在结合几个月测试过程简单的谈谈自动化的优缺点,共享资源。

  自动化适用的用例程度最好的就是参数值校验的用例了,对于自动化脚本来说,无论是脚本语言还是高级语言都可以采用一个模板,就是一个套子,每次使用的时候只需要更换传递的参数即可。这种测试,同样基于最基本的等价类边界值的用例划分,测试设计的基本思想,自动化同样适用。

  其实对于参数值用例校验本身,其集成在功能模块测试之中,每个用例都最原始功能模块的一部分,软件的程序就是这样,我们测试每个功能模块是否有缺陷也就是靠传递参数查看其返回是否达到期望结果。PS:不能向拆开汽车或者X光检查身体那样…………

  自动化测试无法比手工测试发挥更好的地方就是执行用户级用例的时候,具体到执行的时候就是界面测试和用户场景测试,其实这两种测试有交集的,都已直接和用户打交道为主,因为是人,所以自动化即使执行通过不代表易用性等等方面达到要求,还是要具体使用情况。

  自动化测试适用前期没有页面、需要验证程序功能模块的情况,不但能够尽早的发现问题,而且自动化本身的复用性也让后期的回归测试和冒烟测试变得效率十足。对于测试前端页面和用户体验性测试,不建议使用,因为自动化脚本编写和调试本身就很耗时,保证脚本的正确性也需要费些周折,测试脚本执行完成后还需要测易用性测试,时间上需要花费的更多。

posted @ 2011-11-29 11:12 顺其自然EVO 阅读(120) | 评论 (0)编辑 收藏

在防火墙上开放Oracle服务端口的方法

 Oracle服务端口方面会有很多的问题,下面就将为您介绍在防火墙上开放Oracle服务端口的方法,希望对您学习Oracle服务端口方面能有所帮助。

  要使Oracle客户端能正常连接到设置有防火墙的安装在windows上的Oracle服务器,单开放一个1521或自定义的监听端口是不够的。

  我们有的时候需要映射端口远程去访问Oracle数据库,这里有个防火墙的问题,在unix上没有问题,但是在win 平台上却无法正确访问,下面的可以解决这个问题,:

  近来由于工作需要,在Windows XP平台上安装了Oracle9i数据库作为测试之用,一切正常。但当客户机连接服务器时却总是超时,我首先想到了防火墙,当我打开1521端口时,连接操作仍然失败。我又怀疑网络有问题,用telnet server_ip:1521尝试,连接被接受,说明1521端口已经被打开。

  没有办法,查询Oracle资料后才明白,network listener只起一个中介作用,当客户连接它时,它根据配置寻找到相应的数据库实例进程,然后spawned一个新的数据库连接,这个连接端口由network listener传递给客户机,此后客户机就不再和打交道了,即使listener停止了工作。这个新的连接端口是不可预知的,因而会被防火墙阻止。

  Windows Socket2 规范有一个新的特性,就是Shared Socket,所谓共享套接字是指一个进程共享另一个进程的套接字(详见MSDN相关参考)。如果让network listener与数据库服务进程共享套接字,那么连接端口就不会变化。

  如何设置Shared Socket?

  在注册表:HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOME0上新建一个字符串值:USE_SHARED_SOCKET=true。如果安装了多个目录,则每个类似的目录都要设置:HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMEx (x目录编号)

  设置后要求重新启动实例(只重启listener发现没有效果)

  Oracle客户端连接服务器,首先去找1521监听端口,服务器的1521监听端口再向server process进程发出请求,并返回一个随机端口,返回给客户端,客户端再来连接这个端口。 这样就给服务器上的防火墙设置带来了麻烦,这个端口是随机的,如何开放?

  windows平台上的这个问题成了一大难题,很多论坛都有人问,但很少有人能解决。unix平台不用担心,系统自动会解决这个问题. Matalink上提供了三种解决办法,实际上USE_SHARED_SOCKET是最有效最方便的。但经过无数次实现,仍然没有成功,最后终于发现是Oracle 8.1.7的bug 需要打补丁,升级到Oracle 8.1.7.1.2

  需要在MTS模式下(共享模式) Oracle默认是专用模式。

  经试验发现,如果不在init文件中设参数的话,Oracle仍然会要求一个随机端口和1521端口来共同通讯,只是这个随机端口,并不随客户端会话和登录的变化而变化,在没有重启服务器时,是固定的。(试验发现,在专用模式下,每次连接,oracle服务器会按+1方式,提供一个非1521的端口。)所以,还需要在init.ora文件的最后加上一条参数:

  mts_dispatchers="(address=(protocol=tcp)(host=myoradb)(port=1521))(dispatchers=1)"

  设置后要求重新启动实例。

posted @ 2011-11-29 11:08 顺其自然EVO 阅读(305) | 评论 (0)编辑 收藏

关于项目管理的一点体会

“1人100个月完成的项目,不是100个人1个月就可以完成。”

  项目管理是让项目活动中相互竞争的各类制约因素:质量、进度、资源、风险等取得平衡的艺术,同时也是平衡项目干系人的各种需要、关注和期望,带领不同的人朝着相同目标迈进的领导艺术。

  成功的项目管理可以简单理解为:按时、在预算内+满足产品需求+满足质量需求 地完成项目。

  以下是我对项目管理的一点体会记录。

  需求等级

  视觉 A:图片没有分享功能吗?

  技术 K:图片有链接转发分享、微博或邮件形式分享等多种分享,全部开发的话需要推延时间表。

  策划 D:图片只做预览、下载已经足够了,暂时不做分享。

  交互 E:如果我们的用户是基于邮箱用户,图片的邮件分享还是建议做。

  … …

  如果在前期产品需求文档中没有明确定义每个需求的优先等级,或者说项目成员对需求的优先等级没有明确的意识,可能类似的争论会时常发生在项目成员之间,每个人会基于自己对产品目标的理解来考虑这个需求是否要做,什么时候做,做到什么程度而产生分歧,因而增加项目推进的阻力。

  所以在前期产品需求文档中,必须明确定义出每个需求的优先等级,需求的粒度可细化到每个大功能下的子功能需求,如:图片分享功能的转发链接分享、邮件形式分享这样的子功能需求。等级的划分依赖于前期的用户需求调研、产品的预定目标、开发成本、运营计划等;

  一般的需求等级划分:

  P0 -Must have: 如果缺失,产品不能发布

  P1 -Should have: 如果缺失,产品能发布,但不能达到预定目标(功能/性能)

  P2 -Nice to have: 做了则更好

  P3 -Neutral: 对产品没有明显的好处,用户不在意

  … …

  每个需求的优先等级确定之后,产品经理根据产品预定目标、开发成本、运营计划等来定义一个等级分界线,高于或等于这个等级分界线的需求在本期开发,部分根据成本、运营计划等因素调整到下期开发,而低于这个等级分界线的需求则只会在下期开发,这样让全体项目成员对本期要做的需求达成共识。
“1人100个月完成的项目,不是100个人1个月就可以完成。”

  项目管理是让项目活动中相互竞争的各类制约因素:质量、进度、资源、风险等取得平衡的艺术,同时也是平衡项目干系人的各种需要、关注和期望,带领不同的人朝着相同目标迈进的领导艺术。

  成功的项目管理可以简单理解为:按时、在预算内+满足产品需求+满足质量需求 地完成项目。

  以下是我对项目管理的一点体会记录。

  需求等级

  视觉 A:图片没有分享功能吗?

  技术 K:图片有链接转发分享、微博或邮件形式分享等多种分享,全部开发的话需要推延时间表。

  策划 D:图片只做预览、下载已经足够了,暂时不做分享。

  交互 E:如果我们的用户是基于邮箱用户,图片的邮件分享还是建议做。

  … …

  如果在前期产品需求文档中没有明确定义每个需求的优先等级,或者说项目成员对需求的优先等级没有明确的意识,可能类似的争论会时常发生在项目成员之间,每个人会基于自己对产品目标的理解来考虑这个需求是否要做,什么时候做,做到什么程度而产生分歧,因而增加项目推进的阻力。

  所以在前期产品需求文档中,必须明确定义出每个需求的优先等级,需求的粒度可细化到每个大功能下的子功能需求,如:图片分享功能的转发链接分享、邮件形式分享这样的子功能需求。等级的划分依赖于前期的用户需求调研、产品的预定目标、开发成本、运营计划等;

  一般的需求等级划分:

  P0 -Must have: 如果缺失,产品不能发布

  P1 -Should have: 如果缺失,产品能发布,但不能达到预定目标(功能/性能)

  P2 -Nice to have: 做了则更好

  P3 -Neutral: 对产品没有明显的好处,用户不在意

  … …

  每个需求的优先等级确定之后,产品经理根据产品预定目标、开发成本、运营计划等来定义一个等级分界线,高于或等于这个等级分界线的需求在本期开发,部分根据成本、运营计划等因素调整到下期开发,而低于这个等级分界线的需求则只会在下期开发,这样让全体项目成员对本期要做的需求达成共识。

“1人100个月完成的项目,不是100个人1个月就可以完成。”

  项目管理是让项目活动中相互竞争的各类制约因素:质量、进度、资源、风险等取得平衡的艺术,同时也是平衡项目干系人的各种需要、关注和期望,带领不同的人朝着相同目标迈进的领导艺术。

  成功的项目管理可以简单理解为:按时、在预算内+满足产品需求+满足质量需求 地完成项目。

  以下是我对项目管理的一点体会记录。

  需求等级

  视觉 A:图片没有分享功能吗?

  技术 K:图片有链接转发分享、微博或邮件形式分享等多种分享,全部开发的话需要推延时间表。

  策划 D:图片只做预览、下载已经足够了,暂时不做分享。

  交互 E:如果我们的用户是基于邮箱用户,图片的邮件分享还是建议做。

  … …

  如果在前期产品需求文档中没有明确定义每个需求的优先等级,或者说项目成员对需求的优先等级没有明确的意识,可能类似的争论会时常发生在项目成员之间,每个人会基于自己对产品目标的理解来考虑这个需求是否要做,什么时候做,做到什么程度而产生分歧,因而增加项目推进的阻力。

  所以在前期产品需求文档中,必须明确定义出每个需求的优先等级,需求的粒度可细化到每个大功能下的子功能需求,如:图片分享功能的转发链接分享、邮件形式分享这样的子功能需求。等级的划分依赖于前期的用户需求调研、产品的预定目标、开发成本、运营计划等;

  一般的需求等级划分:

  P0 -Must have: 如果缺失,产品不能发布

  P1 -Should have: 如果缺失,产品能发布,但不能达到预定目标(功能/性能)

  P2 -Nice to have: 做了则更好

  P3 -Neutral: 对产品没有明显的好处,用户不在意

  … …

  每个需求的优先等级确定之后,产品经理根据产品预定目标、开发成本、运营计划等来定义一个等级分界线,高于或等于这个等级分界线的需求在本期开发,部分根据成本、运营计划等因素调整到下期开发,而低于这个等级分界线的需求则只会在下期开发,这样让全体项目成员对本期要做的需求达成共识。



posted @ 2011-11-28 13:40 顺其自然EVO 阅读(139) | 评论 (0)编辑 收藏

如何减少bug

通常的做法是通过更多的单元测试 (Unit test) 和code review,使得我们在开发阶段发现更多的问题,从而减少bug数。的确,开发人员经常单元测试,具有良好的测试和编程习惯,在每次check-in之前,或每次打baseline之前,项目组都有代码cross review,同级或跨级评审,自己代码每日评审能大大保证代码质量,在提交给测试组之前就消除大量的bug。但往往发现更大多数的bug是我们通过 Unit test和code review所不能发现的。为什么?

  1、首先是需求的不明确,比如客户原先对软件的部署的需求就是和一般软件一样,没啥特定需求,后来项目进行到后期部署阶段发现有更多的部署需求,比如Failover,并行部署,对vista的兼容性等等。这些都带来的新的问题和代码修改量。

  2、其次是需求理解的偏差,设计理解的偏差,比如一个员工对保险业务不熟悉,去开发保险业务IT系统的时候,往往开发出来的功能和实际业务需求相差很远。对需求理解的偏差,以及对设计理解的偏差,也有部分原因是因为沟通,没有良好的沟通,导致没有倾听客户的诉求和用户的反馈,和客户沟通的问题导致需求偏差,软件没有对客户产生价值,这种bug的比例非常高。

  3、再次是程序员本身能力的限制,比如代码前期都认真经过了单元测试和功能测试,但后期发现运行效率很低,性能不好,原因在于程序员是用他们不熟悉的语言进行开发,而且对性能设计没有经验,开发中根本没有性能上的考虑。如何保证一个程序员进入一个项目开发之前,已经掌握了足够的编程语言知识和技能,已经掌握了足够的业务知识?如果这些程序员经过技术和业务两方面的培训,可能会避免这方面的问题。

  4、最后是没有一套好的研发流程,质量管理体系,和配套的支持工具。这是最大的一个问题。如何找到一个适合自身公司文化和项目情况的process?

  总之,软件开发和编程是一项智力活动,从获得需求、理解需求、程序设计、程序编码(数据结构 + 算法)、单元测试、功能测试、提交的整个过程中,任何一步出现偏差都可能产生bug。

  当然,测试组的严格测试能保证软件的质量,但问题是如何主动防范bug?

  1、程序员的技术能力和经验很重要,比如:代码设计能力,良好的编程习惯,良好的数据结构和算法,编程规范的遵守,随时资源的释放,避免内存泄漏,避免导致性能下降的代码,异常处理,以及对维护、部署、可用性、性能、稳定性的全面,良好的文档和注释习惯等等。另外,项目采用新的架构、框架或技术(例如Spring, Castle, WCF),都会因为程序员不熟悉而引入更多的bug和风险。

  2、程序员的业务积累和经验很重要,大大有助于对需求的理解和把握。这非常关键。例如一个程序员做过老版本的银行清算系统,他不仅熟悉清算业务流程,而且知道老系统存在的问题,就会主动防止这些问题,准备高效的实现新系统。

  3、测试组的测试活动不仅仅是找出bug,而且要通过测试来规范项目开发过程,从而提高软件产品的质量。测试通过了,bug都改完了,项目结束了?其实测试组可以总结和分析下bug产生的原因和分布,这个bug list和分布图交给开发组长和开发人员,可以分析发现开发人员经常哪儿引入bug,从而在以后的开发活动中避免这些问题,实现项目组的积累。其实可能80%的bug分布在20%的模块,因此从各个方面分析bug的根源,可以总结出项目组可以改进的地方。

  最后,从根本上来说,作为软件产品与服务的提供者,只有真正理解客户的业务、顺应客户的需求才能提供令客户满意的产品与服务。应当以一个用户角色的眼光去重新审视为用户提供的技术解决方案和产品,是否是用户所真正关心的,是否真正解决了用户的问题。对于客户而言,最有价值的不是你掌握哪些技术,而是你能帮他们解决哪些问题,产生哪些价值。IBM推行OnDemand随需应变的服务, 因为在当今市场竞争日趋激烈的今天,“求变” 已经是必不可少的生存法则。这个求变的过程,需要软件公司到技术人员的蜕变,从灵活多变的业务,到随需应变的技术,不管客户的业务和管理流程、需求如何变化,技术都只是业务变革的推进动力和实现工具,bug free(无缺陷)的软件背后其实是对业务和需求的深刻理解和行业积累,先进的技术实力,完善的质量管理体系,和软件开发流程。

posted @ 2011-11-28 13:38 顺其自然EVO 阅读(195) | 评论 (0)编辑 收藏

彩票站漏洞引发的软件测试漏测思考

  背景信息:央视:彩票站的惊天秘密,站主利用漏洞中奖2800万,2011年11月24日...本案例的主人公利用漏洞,知道中奖号码后在买,5分钟漏洞造就2800万神话,数千次兑奖福彩未察觉。

  看到这个新闻,不由得让从事软件测试的我们引发很多思考,从某种意义上来说,我不得不佩服这个站主的思维方式,很显然的是,整个福彩的软硬件系统肯定是经过相对严格的软件测试和硬件测试的。那为什么还有出现这么低级的“软件漏测”呢?到底是软件测试的意识有问题?软件测试工程师的责任心有问题?还是软件测试需求分析与设计有问题?(也就是说根本就没有想到这一点?)还是软件测试管理层面的问题?但是不管是哪一个方面的问题,这个事件注定会给整个彩票行业带来深刻的反省和思考,也会给软件行业带来反省和思考,更会给软件测试带来反省和思考。这个联想到淘宝将很多卖家“商品价格被改为1元”软件故障事件来看,不由让人胆寒。在软件测试行业里面来看,相对于其他企业而言,阿里巴巴集团包括淘宝都是比较重视软件测试的,从51Testing企业招聘板块也经常可以看到他们的招聘信息。从这一点来看,中国软件质量、中国软件测试任重而道远。

  分析这两个事情,我发现这里面最根本的问题还是解决软件测试质量根本之道的软件测试需求分析与软件测试设计,一个企业重视测试还是不重视软件测试,软件测试做得好还是不好,第一步也是最重要的一步是解决What to test和How to test。而不是盲目地去做自动化,盲目的测试开发。对于一个优秀的测试工程师而言,首先也是必须要会的就是测试分析与设计。那么接下来,我们来分析下,怎么样才能做好软件测试需求分析与软件测试设计。从福彩的这个漏洞来看,是站主利用了这个漏洞,而不是普通的彩民。那为什么会这样呢?我臆想下,可能当初在测试整个系统的时候更多的是从买彩票的彩民的角度来测试,本没有过多考虑到彩票站站主这样一类用户。在做好软件测试需求分析相关的方法论上有一种方法叫做“关联图分析法”这种方法简单地来说,是从不同的“用户类”的角度来思考,所谓用户类用户类不一定指人,可以把其他应用程序或者系统接口所用到的硬件组件也可以看成是附加用户类成员。有一些受产品影响的人并不一定是产品的直接使用者,而是通过报表或者其他应用程序访问产品的数据和服务,比如站长是区别与彩民之外的另一类用户类。用户类可以是执行者,也可以是应用软件、系统硬件、目标实体、接口实体或者三维空间、时间等等。应用软件:是指用户通过其他软件来操作被测特性的软件。系统硬件:理解范围可以宽些,系统有硬件和软件组成,直接影响被测特性运行的硬件都认为是系统硬件。目标实体:可以理解为影响被测特性的公共模块或者实体。接口实体:是指和被测特性有关联的外界接口实体。三维空间、时间:可以理解为时间、空间、环境对被测特性的影响。比如:这个案例里面的福彩系统在开奖一段时间内在检查。更通俗地说,我们测试一个系统,要关注的是全方位的,比如这个系统影响了谁?谁在用这个系统?谁为这个系统提供数据?谁来维护系统?这个系统在特殊的时间下是否有特殊的用户行为等?

  当然这个案例很容易想到要用这一种方法,其实软件测试的方法和很多,好比软件测试武功的武功秘籍,最重要的是掌握的方法要全面,并且要知道每一种方法的优点、缺点、适应范围,就好比跆拳道中短距离进攻优势很明显,泰拳靠的是力量,柔术一定是贴身的,太极靠的是借力打力的套路。下面一个图可以帮助大家更清晰地了解软件测试的技术体系,其中包括最为重要的方法论体系。当然大家也可以关注我以前的一个文章http://www.51testing.com/index.php?uid-94273-action-viewspace-itemid-197958

版权声明:本文出自 linlinxu 的51Testing软件测试博客:http://www.51testing.com/?94273

原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。

posted @ 2011-11-28 13:27 顺其自然EVO 阅读(587) | 评论 (1)编辑 收藏

自动化测试测试平台策略 之模块交互策略

自动化测试测试平台策略 之模块交互策略

序言:要做一个自动化测试平台,越强大的平台,其模块之间的交互越难,也就是各个模块之间的接口定义越难,而如何采用一种策略去规范各个模块的接口、消息格式和交互方式更是难,这一点,我觉得可以从学习网络协议中找到一丝灵感,那些协议的交互方式以及消息的格式传递都是值得学习的,以前觉得学习协议纯粹是为了了解,现在学习真的是想掌握其几点精华思想,突然能够想象到:一群人在一起思想的碰撞,不断的去总结,去发现,去实用,才有了现在的协议标准。觉得,不一样的领域都去发现才能有所感悟。

  一、自动化测试平台中的模块

  1、软件产品是具有一系列特定功能的组件组成,其系统可以被分为一系列的功能模块,每个模块所特有的信息处理过程都被包含在模块的内部,如同一个“黑箱”,这就是“封装性”,然后模块与模块之间按照一定的规则相连则成了一个复杂的系统(一个系统也可以作为一个模块,去组成更复杂的系统)。

  2、而在自动化测试平台的系统开发中,首先,按照其抽象出来的自动化测试流程和方式划分一系列的功能模块,这些功能模块都能脱离系统独自使用,有的模块独自使用能够提高一些效率(像CLI测试中的基于脚本的自动化测试框架,GUI测试中的基于工具的自动化测试框架等都是一种模块,还有一些测试工具),然后在此些模块的基础上,我们定义一些交互规则,将他们以最好的方式进行安排在自动化测试流水线上,然后提供一个统一管理的界面,慢慢的,就构建成了一个自动化测试平台。(关键点:必须在测试功能模块独自提高效率的基础上,能够在测试流程中加以应用,平台的作用伴随更多的服务,将他们流水线化)

  二、自动化测试平台平台构建难点

  1、如何在测试流程中提炼自动化测试流程,然后抽象出自动化测试流水线。

  2、如何能够统一规划整个测试部门的测试资源,这需要一个服务器去统一存储和调度管理。

  3、然后,很关键的一步:如何将已有的功能测试模块结合起来,即建立一套良好的交互协议和交互消息格式,使其能够很好的交互和互补,真正完成自动化测试流水线的运作。

  三、自动化测试平台模块交互策略分析

  以下策略是自己对测试平台建设中的一些提炼,不一定完全适合,可以参考

  1、策略1:服务注册机制

  即定义一个框架,其框架提供相应的API,所有的模块利用这个API遵循一定的规则都可以作为服务注册到这个框架中,注册的同时,也对该服务的发送和接收的消息进行了规定,不同的消息可以调用其模块不同的功能,然后,模块则可以以这个框架为媒介,以传递消息的方式互相控制。当然,对于数据的存储,需要单独提供一个空间,可以是服务器,也可以是内存机制。

  2、策略2:消息分发统一机制

  即定义一个消息分发模块,模块的交互都会经过这个消息分发模块,这个消息分发模块将消息进行解析,然后传送到对应的模块。

  这里,两种策略都需要统一规定好消息的格式。可以参考网络协议中的一些消息格式,例如一个简单的:

源头

目的头

时间

消息类型

消息内容

  总之,个人上次听过一句话,如果你在一个行业领域已经停滞不前,那么就将眼光放宽,去另外一个领域去看看,往往这样,会激发你无限的灵感。这也许是真理啊,乔布斯不就很爱好艺术吗?马云不就是个金庸迷吗?当然,不是说要去复制,个人觉得,说的是不要闭门造船吧,而是学会提炼共性,在思想中升华吧。

 

posted @ 2011-11-28 13:27 顺其自然EVO 阅读(199) | 评论 (0)编辑 收藏

程序员如何让你的变量名更加精确

程序员如何让你的变量名更加精确

 

字体:        | 上一篇 下一篇 | 打印  | 我要投稿  | 推荐标签: 软件开发 java

关键点

  “别人还能把这个名字理解成什么意思?”通过不断的问自己这个问题来积极检查每一个命名。

  事实上,这种富有创造性的、不断尝试“错误理解”的方法,能够有效的发现歧义的命名,并修正它们。正如本文中的示例,我们将随时通过“骑驴看唱本 ——边走边瞧”的方式来 探讨所见到名字的误解之处,然后选取一个更好的名字。

  示例:Filter()

  假设写了一段代码来操作数据库结果的集合:

  results= Database.all_objects.filter("year <= 2011")

  那么,results包含什么数据呢?

  所有满足year<=2011的对象

  所有不满足year<=2011的对象

  问题的由来是从filter这个有歧义的词开始的,它没有清楚表达它的意思是“选取”还是“剔除”。因此,应该避免使用filter,它太容易造 成误解!

  如果这里想要的效果是“选取”,一个更好的名字是select;如果想要的是“剔除”,更好的名字则是exclude。

  为布尔值取名

  当为布尔值变量命名或者函数返回布尔值的时候,要特别注意真和假所表达出来的真实意思,这里就有一个很危险的例子:

  bool read_password= true;

  这句代码意思取决于当时怎么阅读的(没有其他的意思了),显然这里有两种截然不同的理解:

  需要读密码

  密码已经被读过了

  在这个用例下,做好避免用单词read,可以考虑使用need_password或者user_is_authenticated来代替。

  通常情况下,添加单词is、has、can或者should可以让布尔值的意思更加清晰易懂。

  比如说有个函数叫SpaceLeft(),乍一看,就会想到这个函数返回的值是数字。如果需要明确返回值是布尔值,一个更好的名字是 HasSpaceLeft()。

  还有,尽量避免使用反义短句来命名。例如:

bool disable_ssl= false;

 

  改成如下代码则更容易理解,同时更契合原意:

bool use_ssl= true;

  符合用户期望

  很多名字是带有误导性的,因为对于某个名字,用户自已有一个预想的定义,但是代码的意思可能恰恰不是这个意思。如此情况下,最好作出“让步”并改 变名字,消除 误导性。

  示例:get*()

  许多程序员都在使用这样的编码规范:某个方法以get开头来表达一个“轻量级的访问器”以返回内部成员。违反这个规范将很容易误导用户。 避免下面的例子中java代码段的做法:

public class StatisticsCollector { public void addSample(double x) { ... } public double getMean() { // Iterate through all samples and return total / num_samples } ...}

  这里,getMean的实现是枚举过去所有的数据,并计算其平均值。如果数据量很大的时候,这一步的开销将会是非常大的。但是,一个不了解情况的 程 序员则会很粗心的调用它并且假设这是一个很廉价的调用。

  因此,这个方法应该改名成类似computeMean()这样的,看起来这样就是一个代价高昂的操作了(或者,另一个选择就是改写其实现,变成一 个名副其实的轻量级操作)。

  示例:list::size()

  这里讲一个C++标准库里的命名问题。这段代码导致的结果是,很难定位和修复类似导致服务器龟速运行之类的问题:

void ShrinkList(list<Node>& list, int max_size) { while (list.size()>max_size) { FreeNode(list.back()); list.pop_back(); }}

  这样的bug的导致是作者没有意识到list.size()是一个O(n)复杂度的操作——它挨个计数链表的节点得出总数而不是返回已计算 好的总个数,这将导致ShrintList是一个O(n2) 的操作。

  从技术角度讲,这段代码没有问题,也能通过所有的单元测试。但是当调用ShrintList()并传入一个包含上亿数量级的list时,它可能将 耗费数小时的时间。

  或许你会认为,这个是调用者的错误使用,他/她没有认真仔细的阅读相关的文档!确实是这样的,但是,事实上,这里的list.size()不是一 个恒准时(constant-time)操作,这太意外了!其他所有的C++容器类都是恒准时的size()方法呀。

  假如把size()更名成countSize()或者countElements(),类似的错误就会大大减少了。C++标准库的实现者可能想的 是使用一个size()方法去和其他的容器匹配,像vector和map,这样API的一致性看起来更好。正是由于这样的做了,导致程序员容易误 用并认为这是一个很快的操作,和其他的容器一样!幸运的是,最新的C++标准要求size()是O(1)复杂度。

 

posted @ 2011-11-28 13:23 顺其自然EVO 阅读(167) | 评论 (0)编辑 收藏

查看Linux系统的平均负载

查看Linux系统的平均负载


1、Linux系统的平均负载的概念

  有时候我们会觉得系统响应很慢,但是又找不到原因,这时就要查看平均负载了,看它是否有大量的进程在排队等待。特定时间间隔内运行队列中的平均进程数可以反映系统的繁忙程度,所以我们通常会在自己的网站或系统变慢时第一时间查系统的负载,即CPU的平均负载。

  2、查看平均负载

  究竟应该如何查看平均负载呢?最简单的命令是uptime,如下所示:

  • [root@localhost ~]# uptime  
  •  11:31:11 up 11 days, 19:01, 2 users, load average: 0.02, 0.01, 0.00
  •   目前的主流服务器都是双四核,有相当强悍的CPU,做一般的应用服务的话,Linux系统的负载这块倒不用我们担心。

      还可以用w命令来查看,顺便可以查看一下系统当前有哪些用户,他们占用了哪些终端,如下所示:

  • [root@localhost ~]# w  
  •  11:33:00 up 11 days, 19:03, 2 users, load average: 0.00, 0.00, 0.00  
  • USER TTY FROM LOGIN@  IDLE  JCPU  PCPU WHAT  
  • root pts/1113.57.224.3 09:032:11m 0.04s 0.04s -bash  
  • root pts/2113.57.224.3 11:310.00s 0.02s 0.00s w
  •   另外,还有动态命令top,这个命令也可以反映系统负载情况。在下面的命令提示中,我们只关心加粗字体部分。

  • [root@localhost ~]# top  
  • top - 11:37:47 up 11 days, 19:08, 2 users, load average: 0.00, 0.00, 0.00  
  • Tasks: 122 total,  1 running, 121 sleeping,  0 stopped,  0 zombie  
  • Cpu(s): 0.1%us, 0.0%sy, 0.0%ni, 99.9%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st  
  • Mem:  4044136k total, 1435504k used, 2608632k free,  274740k buffers  
  • Swap: 8193140k total,0k used, 8193140k free,  941884k cached
  •   上面加粗字体显示的内容是什么意思呢?再通过uptime查看一下。

  • [root@localhost ~]# uptime  
  •  11:39:36 up 11 days, 19:16, 1 user, load average: 0.09, 0.03, 0.01
  •   原来它所表示的是过去的1分钟、5分钟和15分钟内进程队列中的平均进程数量。

      那么,如何衡量当前系统是否负载过高呢?可以从以下几点来考虑。

      如果每个CPU(可以按CPU核心的数量计算)当前的活动进程数不大于3,则系统性能良好。

      如果每个CPU当前的活动进程数不大于4,表示可以接受。

      如果每个CPU当前的活动进程数大于5,则系统性能问题严重。

      还可以结合vmstat命令来判断我们的系统是否过于繁忙,如果确定很繁忙的话,就要考虑是否更换服务器或增加CPU的个数了。总结如下:

      如果r经常大于3或4,且id经常少于50,则表示CPU的负荷很重。

      在上面例子中,我的服务器是PowerEdge 2850,CPU是双核双线程的,则0.09/2=0.045(即负载值/真实CPU个数),此系统的CPU负载基本可以忽略了。事实上,现在主流服务器的CPU都很强悍,如果不是应用虚拟化等特殊场景,基本上负载都很小。

      按照前面的计算公式,我所配置Nagios报警的CPU负载阈值为CPU核心的数量(即CPU的物理个数×核数)。还是以我的服务器PowerEdge 2850为例,其CPU核心的数量为2×2=4,则设置报警值为4。这样设置是合理的,因为毕竟不是每个应用服务器的CPU都支持多核心,毕竟整个网站中还有些性能比较弱的服务器是用来做备份的。

    posted @ 2011-11-28 13:05 顺其自然EVO 阅读(323) | 评论 (0)编辑 收藏

    内部类引用局部变量与外部类成员变量的问题思考

    昨天有一个比较爱思考的同事和我提起一个问题:为什么匿名内部类使用的局部变量和参数需要final修饰,而外部类的成员变量则不用?对这个问题我一直作为默认的语法了,木有仔细想过为什么(在分析完后有点印象在哪本书上看到过,但是就是没有找到,难道是我的幻觉?呵呵)。虽然没有想过,但是还是借着之前研究过字节码的基础上,分析了一些,感觉上是找到了一些答案,分享一下;也希望有大牛给指出一些不足的地方。

      假如我们有以下的代码:

  • interface Printer { 
  •     public void print(); 
  • class MyApplication { 
  •     private int field = 10
  •      public void print(final Integer param) { 
  •         final long local = 100
  •         final long local2 = param.longValue() + 100
  •         Printer printer = new Printer() { 
  •             @Override 
  •             public void print() { 
  •                 System.out.println("Local value: " + local); 
  •                 System.out.println("Local2 value: " + local2); 
  •                 System.out.println("Parameter: " + param); 
  •                 System.out.println("Field value: " + field); 
  •             } 
  •         }; 
  •         printer.print(); 
  •     } 
  • }
  •   这里因为param要在匿名内部类的print()方法中使用,因而它要用final修饰;local/local2是局部变量,因而也需要final修饰;而field是外部类MyApplication的字段,因而不需要final修饰。这种设计是基于什么理由呢?

      我想这个问题应该从Java是如何实现匿名内部类的。其中有两点:

      1、匿名内部类可以使用外部类的变量(局部或成员变来那个)。

      2、匿名内部类中不同的方法可以共享这些变量。

      根据这两点信息我们就可以分析,可能这些变量会在匿名内部类的字段中保存着,并且在构造的时候将他们的值/引用传入内部类。这样就可以保证同时实现上述两点了。

      事实上,Java就是这样设计的,并且所谓匿名类,其实并不是匿名的,只是编译器帮我们命名了而已。这点我们可以通过这两个类编译出来的字节码看出来:

  • // Compiled from Printer.java (version 1.6 : 50.0, super bit) 
  • class levin.test.anonymous.MyApplication$1 implements levin.test.anonymous.Printer { 
  •    
  •   // Field descriptor #8 Llevin/test/anonymous/MyApplication; 
  •   final synthetic levin.test.anonymous.MyApplication this$0
  •    
  •   // Field descriptor #10 J 
  •   private final synthetic long val$local2; 
  •    
  •   // Field descriptor #12 Ljava/lang/Integer; 
  •   private final synthetic java.lang.Integer val$param; 
  •    
  •   // Method descriptor #14 (Llevin/test/anonymous/MyApplication;JLjava/lang/Integer;)V 
  •   // Stack: 3, Locals: 5 
  •   MyApplication$1(levin.test.anonymous.MyApplication arg0, long arg1, java.lang.Integer arg2); 
  •      0  aload_0 [this
  •      1  aload_1 [arg0] 
  •      2  putfield levin.test.anonymous.MyApplication$1.this$0 : levin.test.anonymous.MyApplication [16
  •      5  aload_0 [this
  •      6  lload_2 [arg1] 
  •      7  putfield levin.test.anonymous.MyApplication$1.val$local2 : long [18
  •     10  aload_0 [this
  •     11  aload 4 [arg2] 
  •     13  putfield levin.test.anonymous.MyApplication$1.val$param : java.lang.Integer [20
  •     16  aload_0 [this
  •     17  invokespecial java.lang.Object() [22
  •     20  return 
  •       Line numbers: 
  •         [pc: 0, line: 1
  •         [pc: 16, line: 13
  •       Local variable table: 
  •         [pc: 0, pc: 21] local: this index: 0 type: new levin.test.anonymous.MyApplication(){} 
  •    
  •   // Method descriptor #24 ()V 
  •   // Stack: 4, Locals: 1 
  •   public void print(); 
  •      0  getstatic java.lang.System.out : java.io.PrintStream [30
  •      3  ldc <String "Local value: 100"> [36
  •      5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38
  •      8  getstatic java.lang.System.out : java.io.PrintStream [30
  •     11  new java.lang.StringBuilder [44
  •     14  dup 
  •     15  ldc <String "Local2 value: "> [46
  •     17  invokespecial java.lang.StringBuilder(java.lang.String) [48
  •     20  aload_0 [this
  •     21  getfield levin.test.anonymous.MyApplication$1.val$local2 : long [18
  •     24  invokevirtual java.lang.StringBuilder.append(long) : java.lang.StringBuilder [50
  •     27  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54
  •     30  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38
  •     33  getstatic java.lang.System.out : java.io.PrintStream [30
  •     36  new java.lang.StringBuilder [44
  •     39  dup 
  •     40  ldc <String "Parameter: "> [58
  •     42  invokespecial java.lang.StringBuilder(java.lang.String) [48
  •     45  aload_0 [this
  •     46  getfield levin.test.anonymous.MyApplication$1.val$param : java.lang.Integer [20
  •     49  invokevirtual java.lang.StringBuilder.append(java.lang.Object) : java.lang.StringBuilder [60
  •     52  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54
  •     55  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38
  •     58  getstatic java.lang.System.out : java.io.PrintStream [30
  •     61  new java.lang.StringBuilder [44
  •     64  dup 
  •     65  ldc <String "Field value: "> [63
  •     67  invokespecial java.lang.StringBuilder(java.lang.String) [48
  •     70  aload_0 [this
  •     71  getfield levin.test.anonymous.MyApplication$1.this$0 : levin.test.anonymous.MyApplication [16
  •     74  invokestatic levin.test.anonymous.MyApplication.access$0(levin.test.anonymous.MyApplication) : int [65
  •     77  invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [71
  •     80  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54
  •     83  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38
  •     86  return 
  •       Line numbers: 
  •         [pc: 0, line: 16
  •         [pc: 8, line: 17
  •         [pc: 33, line: 18
  •         [pc: 58, line: 19
  •         [pc: 86, line: 20
  •       Local variable table: 
  •         [pc: 0, pc: 87] local: this index: 0 type: new levin.test.anonymous.MyApplication(){} 
  •   Inner classes: 
  •     [inner class info: #1 levin/test/anonymous/MyApplication$1, outer class info: #0 
  •      inner name: #0, accessflags: 0 default
  •   Enclosing Method: #66  #77 levin/test/anonymous/MyApplication.print(Ljava/lang/Integer;)V 
  • }
  •  

     这些字段在构造函数中赋值,而构造函数则是在MyApplication.print()方法中调用。

      由此,我们可以得出一个结论:Java对匿名内部类的实现是通过编译器来支持的,即通过编译器帮我们产生一个匿名类的类名,将所有在匿名类中用到的局部变量和参数做为内部类的final字段,同是内部类还会引用外部类的实例。其实这里少了local的变量,这是因为local是编译器常量,编译器对它做了替换的优化。

      其实Java中很多语法都是通过编译器来支持的,而在虚拟机/字节码上并没有什么区别,比如这里的final关键字,其实细心的人会发现在字节码中,param参数并没有final修饰,而final本身的很多实现就是由编译器支持的。类似的还有Java中得泛型和逆变、协变等。这是题外话。

      有了这个基础后,我们就可以来分析为什么有些要用final修饰,有些却不用的问题。

      首先我们来分析local2变量,在”匿名类”中,它是通过构造函数传入到”匿名类”字段中的,因为它是基本类型,因而在够着函数中赋值时(撇开对函数参数传递不同虚拟机的不同实现而产生的不同效果),它事实上只是值的拷贝;因而加入我们可以在”匿名类”中得print()方法中对它赋值,那么这个赋值对外部类中得local2变量不会有影响,而程序员在读代码中,是从上往下读的,所以很容易误认为这段代码赋值会对外部类中得local2变量本身产生影响,何况在源码中他们的名字都是一样的,所以我认为了避免这种confuse导致的一些问题,Java设计者才设计出了这样的语法。

      对引用类型,其实也是一样的,因为引用的传递事实上也只是传递引用的数值(简单的可以理解成为地址),因而对param,如果可以在”匿名类”中赋值,也不会在外部类的print()后续方法产生影响。虽然这样,我们还是可以在内部类中改变引用内部的值的,如果引用类型不是只读类型的话;在这里Integer是只读类型,因而我们没法这样做。(如果学过C++的童鞋可以想想常量指针和指针常量的区别)。

      现在还剩下最后一个问题:为什么引用外部类的字段却是可以不用final修饰的呢?细心的童鞋可能也已经发现答案了,因为内部类保存了外部类的引用,因而内部类中对任何字段的修改都回真实的反应到外部类实例本身上,所以不需要用final来修饰它。

      这个问题基本上就分析到这里了,不知道我有没有表达清楚了。

      加点题外话吧。

      首先是,对这里的字节码,其实还有一点可以借鉴的地方,就是内部类在使用外部类的字段时不是直接取值,而是通过编译器在外部类中生成的静态的access$0()方法来取值,我的理解,这里Java设计者想尽量避免其他类直接访问一个类的数据成员,同时生成的access$0()方法还可以被其他类所使用,这遵循了面向对象设计中的两个重要原则:封装和复用。

      另外,对这个问题也让我意识到了即使是语言语法层面上的设计都是有原因可循的,我们要善于多问一些为什么,理解这些设计的原因和局限,记得曾听到过一句话:知道一门技术的局限,我们才能很好的理解这门技术可以用来做什么。也只有这样我们才能不断的提高自己。在解决了这个问题后,我突然冒出了一句说Java这样设计也是合理的。是啊,语法其实就一帮人创建的一种解决某些问题的方案,当然有合理和不合理之分,我们其实不用对它视若神圣。

      之前有进过某著名高校的研究生群,即使在那里,码农论也是甚嚣尘上,其实码农不码农并不是因为程序员这个职位引起的,而是个人引起的,我们要不断理解代码内部的本质才能避免一直做码农的命运那。个人愚见而已,呵呵。

     

     

    posted @ 2011-11-28 12:00 顺其自然EVO 阅读(166) | 评论 (0)编辑 收藏

    在处理bug时,你有适合自己的一套降低风险的策略么?

     修正一个bug的风险到底有多大?或许,你会说,这要看bug是发生在什么地方,的确,UI层的样式问题、后台逻辑调用层的错误、数据访问层的异常、数据库级别函数或存储过程的修改……一个bug产生的影响可能微乎其微,当然也可能会影响广泛,甚至影响到程序架构!

      撇开比较极端的情况,今天想要说的是我们日常工作中遭遇频率最高的一类bug:主要发生在UI层、数据逻辑层的常见bug。对于这种bug,我们又有过多少次因忽略其上下文关联,或者没有添加完整的条件验证和条件处理,导致程序异常,不得不再次修改的经历呢?

      这里并不是要批判我们的“粗心大意”,只是要说明这样一个事实:作为一名开发人员,在处理一个bug的时候,很容易因过于关注bug的细节而“忽视”与其关联的上下文;或在没有完全理解原代码工作完整工作机制的情况下就动手修改bug,导致问题层出不穷……相对前者来说,后者的影响更加的广泛,所以,一般情况下,团队Leader都会指派对某功能模块最为熟悉的人员来处理相关bug,这其实就是一种最为常见的减少风险的方法。

      根据自己在类似问题上多次碰壁经验来看,最为成熟和有效的方法,其实就是从完善我们自己修改bug的流程开始的!为什么这么说呢?打个比方,老婆昨天去医院拔牙,后来把医生的单据给我看,发现医生也是在按照成熟的流程进行作业的,可见一个相对成熟的流程对于我们处理问题是多么地重要!

      回到咱们的主题,处理bug时,我们是不是按照一个自己认为合理的流程进行的呢?我想每个人可能有属于自己的流程,这里仅仅是自己的一点儿看法,仅供参考:

      (1)打开bug列表,你可能使用的是bug tracker或其他bug跟踪工具,这个无所谓。查找你负责的bug项。

      (2)逐条地对bug进行处理,我比较倾向于将当前处理的bug再拷贝一份到自己的bug list文档中,我会添加一些个人的备注,例如,bug的原因,目前的进展,是否需要进行再次验证或与产品经理进行讨论等等,主要是方便自己对bug进行完整的跟踪。

      (3)仔细阅读bug标题和详细内容明细,一般情况下,带注释的截图是非常直观的,个人比较喜欢。如果有疑问的话,需要马上找提交bug的测试人员进行确认,并注意记录发生bug时的关键数据。

      (4)在自己的开发机器上重现bug,起初应该尽可能地按照测试人员提供的关键步骤进行操作,以便迅速重现,如果经过简单分析就可以推断问题的根源的话,可以常识性地进行一些bug验证。

      (5)开启调试功能,重现bug并跟踪代码,找出问题的原因,一般情况下,如果是因对象不存在或赋值错误导致的异常,我们可以直接修正,如果涉及到数据异常的情况,可能我们就需要对产生bug的代码捎带其上下文进行一下排查,因为很有可能是因为数据提供方或者处理方导致了最终的异常。

      (6)确定问题根源,并进行修改后,我们需要进行自我验证!这一步是非常关键的,以至于大家可能很容易忽视!自己也是常常因为觉得一个bug并不难,于是顺手就改了,应该不会有啥问题,然后就check in了,后来的情况大家想必也猜到了,bug被测试打了回来,这还没什么,如果要是需要直接提交给实施或者客户的程序,那后果可就不堪设想啦!这里还有一点需要注意,我们不仅要按照原bug关键步骤进行重复测试,同时还要对相关的流程进行排查性测试,因为我们的修改很有可能影响到周边相关的功能,而当我们对此功能模块不是太熟悉的时候,这种风险尤其大,必须要格外留意。

      (7)再经过上面一步非常重要的自我验证后,我们就可以提交我们的代码了,这里同样有一个很容易被忽略的细节:填写bug备注。因为这是别人了解你此次提交更改的唯一途径,如果马马虎虎,或者是干脆不写的话,那么你的修改出现问题,光追查原因就要浪费很多的时间,如果那哥们如果已经远走高飞以后,那就更加的棘手了!为别人着想也就是为自己着想。

      (8)及时向相关测试人员提供你此次修改的一些情况,尤其是如果涉及范围较大,需要进行排查性验证的时候,一定要向测试人员交待清楚,因为尽管我们做了测试,但依旧有可能有所遗漏。

      (9)定期查看bug列表,跟踪自己负责的bug,并及时更新自己的bug list文档。

      以上就是自己在处理bug时采取的一般流程,自从自己将这个流程形成文字后,遇到的因bug修改不完全导致的问题明显减少了,因为这些问题都在自己进行自我测试及排他验证的时候发现了,并在提交之前都进行了处理。以前自己因为这样的情况经常发生,经常会感到非常气馁,也怀疑过自己的能力啥的,后来经过一个做测试的哥们提醒才豁然开朗,每个人都会遇到“焦点关注”的问题,因为过于关注于技术细节而忽视前后逻辑关联,这也是造成这个问题的主因。所以说,自己要努力通过完善合理的流程来减少其造成的后果,而不是盲目地进行否认~

      希望能对你有所帮助~

    posted @ 2011-11-25 18:07 顺其自然EVO 阅读(169) | 评论 (0)编辑 收藏

    仅列出标题
    共394页: First 上一页 356 357 358 359 360 361 362 363 364 下一页 Last 
    <2024年11月>
    272829303112
    3456789
    10111213141516
    17181920212223
    24252627282930
    1234567

    导航

    统计

    常用链接

    留言簿(55)

    随笔分类

    随笔档案

    文章分类

    文章档案

    搜索

    最新评论

    阅读排行榜

    评论排行榜