您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 人事档案/员工关系 > 高效程序员的45个习惯:敏捷开发修炼之道(选载)
动态评估取舍——高效程序员的45个习惯之一2009-11-1915:59:37标签:程序员习惯效率动态[推送到技术圈]动态评估取舍——高效程序员的45个习惯之一“性能、生产力、优雅、成本以及上市时间,在软件开发过程中都是至关重要的因素。每一项都必须达到最理想状态。”可能曾经身处这样的团队:管理层和客户将很大一部分注意力都放在应用的界面展示上。也有这样的团队,其客户认为性能表现非常重要。在团队中,你可能会发现,有这样一个开发主管或者架构师,他会强调遵守“正确”的范式比其他任何事情都重要。对任何单个因素如此独断地强调,而不考虑它是否是项目成功的必要因素,必然导致灾难的发生。强调性能的重要性情有可原,因为恶劣的性能表现会让一个应用在市场上铩羽而归。然而,如果应用的性能已经足够好了,还有必要继续投入精力让其运行得更快一点吗?大概不用了吧。一个应用还有很多其他方面的因素同样重要。与其花费时间去提升千分之一的性能表现,也许减少开发投入,降低成本,并尽快让应用程序上市销售更有价值。举例来说,考虑一个必须要与远程Windows服务器进行通讯的.NETWindows应用程序。可以选择使用.NETRemoting技术或WebServices来实现这个功能。现在,针对使用WebServices的提议,有些开发者会说:“我们要在Windows之间进行通信,通常此类情况下,推荐使用.NETRemoting。而且,WebServices很慢,我们会遇到性能问题。”嗯,一般来说确实是这样。然而,在这个例子中,使用WebServices很容易开发。对WebServices的性能测试表明XML文档很小,并且相对应用程序自己的响应时间来讲,花在创建和解析XML上的时间几乎可以忽略不计。使用WebServices不但可以在短期内节省开发时间,且在此后团队被迫使用第三方提供的服务时,WebServices也是个明智的选择。Andy说。。。过犹不及我曾经遇到这样一个客户,他们坚信可配置性的重要性,致使他们的应用有大概10000个可配置变量。新增代码变得异常艰难,因为要花费大量时间来维护配置应用程序和数据库。但是他们坚信需要这种程度的灵活性,因为每个客户都有不同的需求,需要不同的设置。可实际上,他们只有19个客户,而且预计将来也不会超过50个。他们并没有很好地去权衡。考虑这样一个应用,从数据库中读取数据,并以表格方式显示。你可以使用一种优雅的、面向对象的方式,从数据库中取数据,创建对象,再将它们返回给UI层。在UI层中,你再从对象中拆分出数据,并组织为表格方式显示。除了看起来优雅之外,这样做还有什么好处吗?也许你只需要让数据层返回一个dataset或数据集合,然后用表格显示这些数据即可。这样还可以避免对象创建和销毁所耗费的资源。如果需要的只是数据展示,为什么要创建对象去自找麻烦呢?不按书上说的OO方式来做,可以减少投入,同时获得性能上的提升。当然,这种方式有很多缺点,但问题的关键是要多长个心眼儿,而不是总按照习惯的思路去解决问题。总而言之,要想让应用成功,降低开发成本与缩短上市时间,二者的影响同样重要。由于计算机硬件价格日益便宜,处理速度日益加快,所以可在硬件上多投入以换取性能的提升,并将节省下来的时间放在应用的其他方面。当然,这也不完全对。如果硬件需求非常庞大,需要一个巨大的计算机网格以及众多的支持人员才能维持其正常运转(比如类似Google那样的需求),那么考虑就要向天平的另一端倾斜了。但是谁来最终判定性能表现已经足够好,或是应用的展现已经足够“炫”了呢?客户或是利益相关者必须进行评估,并做出相关决定(见第45页中习惯10)。如果团队认为性能上还有提升的空间,或者觉得可以让某些界面看起来更吸引人,那么就去咨询一下利益相关者,让他们决定应将重点放在哪里。没有适宜所有状况的最佳解决方案。你必须对手上的问题进行评估,并选出最合适的解决方案。每个设计都是针对特定问题的——只有明确地进行评估和权衡,才能得出更好的解决方案。没有最佳解决方案(Nobestsolution)动态评估权衡考虑性能、便利性、生产力、成本和上市时间。如果性能表现足够了,就将注意力放在其他因素上。不要为了感觉上的性能提升或者设计的优雅,而将设计复杂化。切身感受即使不能面面俱到,你也应该觉得已经得到了最重要的东西——客户认为有价值的特性。平衡的艺术如果现在投入额外的资源和精力,是为了将来可能得到的好处,要确认投入一定要得到回报(大部分情况下,是不会有回报的)。真正的高性能系统,从一开始设计时就在向这个方向努力。过早的优化是万恶之源。过去用过的解决方案对当前的问题可能适用,也可能不适用。不要事先预设结论,先看看现在是什么状况。【连载】优秀程序员的45个习惯之25——代码要清晰地表达意图2009-12-2916:52:19标签:[推送到技术圈]代码要清晰地表达意图——高效程序员的45个习惯之一“可以工作而且易于理解的代码挺好,但是让人觉得聪明更加重要。别人给你钱是因为你脑子好使,让我们看看你到底有多聪明。”Hoare谈软件设计C.A.R.Hoare设计软件有两种方式。一种是设计得尽量简单,并且明显没有缺陷。另一种方式是设计得尽量复杂,并且没有明显的缺陷。我们大概都见过不少难以理解和维护的代码,而且(最坏的是)还有错误。当开发人员们像一群旁观者见到UFO一样围在代码四周,同样也感到恐惧、困惑与无助时,这个代码的质量就可想而知了。如果没有人理解一段代码的工作方式,那这段代码还有什么用呢?开发代码时,应该更注重可读性,而不是只图自己方便。代码被阅读的次数要远远超过被编写的次数,所以在编写的时候值得花点功夫让它读起来更加简单。实际上,从衡量标准上来看,代码清晰程度的优先级应该排在执行效率之前。例如,如果默认参数或可选参数会影响代码可读性,使其更难以理解和调试,那最好明确地指明参数,而不是在以后让人觉得迷惑。在改动代码以修复bug或者添加新功能时,应该有条不紊地进行。首先,应该理解代码做了什么,它是如何做的。接下来,搞清楚将要改变哪些部分,然后着手修改并进行测试。作为第1步的理解代码,往往是最难的。如果别人给你的代码很容易理解,接下来的工作就省心多了。要敬重这个黄金法则,你欠他们一份情,因此也要让你自己的代码简单、便于阅读。明白地告诉阅读程序的人,代码都做了什么,这是让其便于理解的一种方式。让我们看一些例子。coffeeShop.PlaceOrder(2);通过阅读上面的代码,可以大致明白这是要在咖啡店中下一个订单。但是,2到底是什么意思?是意味着要两杯咖啡?要再加两次?还是杯子的大小?要想搞清楚,唯一的方式就是去看方法定义或者文档,因为这段代码没有做到清晰易懂。所以我们不妨添加一些注释。coffeeShop.PlaceOrder(2/*largecup*/);现在看起来好一点了,但是注释有时候是用来对写得很差的代码进行补偿的(见第105页中习惯26:用代码沟通)。Java5与.NET中有枚举值的概念,我们不妨使用一下。使用C#,我们可以定义一个名为CoffeeCupSize的枚举,如下所示。publicenumCoffeeCupSize{Small,Medium,Large}接下来就可以用它来下单要咖啡了。coffeeShop.PlaceOrder(CoffeeCupSize.Largxe);这段代码就很明白了,我们是要一个大杯[①]的咖啡。作为一个开发者,应该时常提醒自己是否有办法让写出的代码更容易理解。下面是另一个例子。Line1publicintcompute(intval)-{-intresult=val1;-//...morecode...5returnresult;-}第3行中的位移操作符是用来干什么的?如果善于进行位运算,或者熟悉逻辑设计或汇编编程,就会明白我们所做的只是把val的值乘以2。PIE原则所写的代码必须明确表达你的意图,而且必须富有表现力。这样可以让代码更易于被别人阅读和理解。代码不让人迷惑,也就减少了发生潜在错误的可能。代码要清晰地表达意图。但对没有类似背景的人们来说,又会如何——他们能明白吗?也许团队中有一些刚刚转行做开发、没有太多经验的成员。他们会挠头不已,直到把头发抓下来②]。代码执行效率也许很高,但是缺少明确的意图和表现力。用位移做乘法,是在对代码进行不必要且危险的性能优化。result=val*2看起来更加清晰,也可以达到目的,而且对于某种给定的编译器来说,可能效率更高(积习难改,见第34页的习惯7)。不要表现得好像很聪明似的,要遵循PIE原则:代码要清晰地表达意图。要是违反了PIE原则,造成的问题可就不只是代码可读性那么简单了——它会影响到代码的正确性。下列代码是一个C#方法,试图同步对CoffeeMaker中MakeCoffee()方法进行调用。PublicvoidMakeCoffee(){lock(this){//...operation}}这个方法的作者想设置一个临界区(criticalsection)——任何时候最多只能有一个线程来执行operation中的代码。要达到这个目的,作者在CoffeeMaker实例中声明了一个锁。一个线程只有获得这个锁,才能执行这个方法。(在Java中,会使用synchronized而不是lock,不过想法是一样的。)对于Java或.NET程序员来说,这样写顺理成章,但是其中有两个小问题。首先,锁的使用影响范围过大;其次,对一个全局可见的对象使用了锁。我们进一步来看看这两个问题。假设Coffeemaker同时可以提供热水,因为有些人希望早上能够享用一点伯爵红茶。我想同步GetWater()方法,因此调用其中的lock(this)。这会同步任何在CoffeeMaker上使用lock的代码,也就意味着不能同时制作咖啡以及获取热水。这是开发者原本的意图吗?还是锁的影响范围太大了?通过阅读代码并不能明白这一点,使用代码的人也就迷惑不已了。同时,MakeCoffee()方法的实现在CoffeeMaker对象上声明了一个锁,而应用的其他部分都可以访问CoffeeMaker对象。如果在一个线程中锁定了CoffeeMaker对象实例,然后在另外一个线程中调用那个实例之上的MakeCoffee()方法呢?最好的状况也会执行效率很差,最坏的状况会带来死锁。让我们在这段代码上应用PIE原则,通过修改让它变得更加明确吧。我们不希望同时有两个或更多的线程来执行MakeCoffee()方法。那为什么不能为这个目的创建一个对象并锁定它呢?PrivateobjectmakeCoffeeLock=newObject();PublicvoidMakeCoffee(){lock(makeCoffeeLock){//...operation}}这段代码解决了上面的两个问题——我们通过指定一个外部对象来进行同步操作,而且更加明确地表达了意图。在编写代码时,应该使用语言特性来提升表现力。使用方法名来传达意向,对方法参数的命名要帮助读者理解背后的想法。异常传达的信息是哪些可能会出问题,以及如何进行防御式编程,要正确地使用和命名异常。好的编码规范可以让代码变得易于理解,同时减少不必要的注释和文档。要编写清晰的而不是讨巧的代码向代码阅读者明确表明你的意图。可读性差的代码一点都不聪明。切身感受应该让自己或团队的其他任何人,可以读懂自己一年前写的代码,而且只读一遍就知道它的运行机制。平衡的艺术现在对你显而易见的事情,对别人可能并不显然,对于一年以后的你来说,也不一定显然。不妨将代码视作不知道会在未来何时打开的一个时间胶囊。不要明日复明日。如果现在不做的话,以后你也不会做的。有意图的编程并不是意味着创建更多的类或者类型。这不是进行过分抽象的理由。使用符合当时情形的耦合。例如,通过散列表进行松耦合,这种方式适用于在实际状况中就是松耦合的组件。不要使用散列表存储紧密耦合的组件,因为这样没有明确表示出你的意图。[①]对星巴克的粉丝来说,这是指venti。[②]没错,那
本文标题:高效程序员的45个习惯:敏捷开发修炼之道(选载)
链接地址:https://www.777doc.com/doc-4852605 .html