代码!代码!
不觉已经写了七年多的代码,但这七年来自己爱写的代码确寥寥无几,许多时间都是在厌倦与烦躁中写代码为了完成工作。而我自认为我是个爱写代码的程序员,但现实中太多的代码与我的意愿相差甚远,最近的工作遇到太多东西想要吐槽。
现在维护的Solution中有300多个project,一个站点在公司这台4G的Celeron机上编译一次就需要30~40分钟,等页面打开双需要10分钟左右,一天的工作基本上是编译、等待页面加载…… 也许要也说:
“这多好,不用写多少代码了!”
但在产品经理和项目经里眼中却是:“这个东西多么简单,改一点点就OK了”
于是程序员下场是:花二至三倍的时间来完成工作,加班!
Solution中的代码已经历史悠久,经过数代程序员修改,下代程序程序员不知道上代程序员对Solution的结构设计,而上代程序员部分早已离职,留下来的已升迁至高层,也不会关注代码这种琐碎的事。所以新员工接到任务需要修改代码时,通常是F12或调试找到代码即修,由于不了解Solution结构,修改代码时经常会将原有的逻辑结构修改,长期积累使Solotion的结构设计远远偏离了原始设计,函数漫天飞、跨层代码访问、Project交差引用、代码冗余度高、无效代码与代码注释堆积…… 这种情况下我们需要重构代码。代码重构是一门学问,对于一个历史悠久的项目,积累了多年的业务的产品来说更是一个庞大的工程。如果这个项目还有足够长的生命周期或发展前景,那公司层面就应该介入,重构甚至是重写代码。对于大多数程序员看来,没有力量来改善这种状况,能做的估计只有吐槽或选择离职了。
我想吐槽的重点不是以上这种情况,做为一个码农,我更想吐槽代码的细节。 我希望看到的代码清爽,最好像夏日里清纯的妹子一样养眼。而最近大半年来在携程的工作让想吐槽地方太多,接下来让我们见识下几处吐槽点
1. 冗余 在我们的代码里随处可见
SOE.Flights[i].RefNote = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Refnote; SOE.Flights[i].RerNote = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Rernote; SOE.Flights[i].EndNote = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Endnote; SOE.Flights[i].NonEnd = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Nonend; SOE.Flights[i].NonRef = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Nonref; SOE.Flights[i].NonRer = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Nonrer; SOE.Flights[i].E_EndNote = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Endnote_en; SOE.Flights[i].E_RefNote = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Refnote_en; SOE.Flights[i].E_RerNote = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Rernote_en; SOE.Flights[i].Refundfeeformulaid = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Refundfeeformulaid; SOE.Flights[i].UpGrade = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.CanUpGrade ? "T" : "F"; SOE.Flights[i].FlightDesc = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy.Remarks;
这段代码为什么不将flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy定义为一个变量?改为下面这种写法是不是觉得心情舒畅了,胸口不闷了!中午可以多吃一碗饭了?
var policy = flightsInfo[i].Flight.SubClasses[0].Prices[0].StandardPolicy; SOE.Flights[i].RefNote = policy.Refnote; SOE.Flights[i].RerNote = policy.Rernote; SOE.Flights[i].EndNote = policy.Endnote; SOE.Flights[i].NonEnd = policy.Nonend; SOE.Flights[i].NonRef = policy.Nonref; SOE.Flights[i].NonRer = policy.Nonrer; SOE.Flights[i].E_EndNote = policy.Endnote_en; SOE.Flights[i].E_RefNote = policy.Refnote_en; SOE.Flights[i].E_RerNote = policy.Rernote_en; SOE.Flights[i].Refundfeeformulaid = policy.Refundfeeformulaid; SOE.Flights[i].UpGrade = policy.CanUpGrade ? "T" : "F"; SOE.Flights[i].FlightDesc = policy.Remarks;
2. 参数过多,代码不换行
function SelectFlight(flight, subclass, price, pricetype, policyid, hasairhotelprice, IsAllRoutesSupportDeliver, producttype, routeindex, canseparatesale, rspecialtype, rpolicyid, rprice1, rprice2, rairlinerate, rpricetype, iscansell, FlightListItemID)
submitFlight(SearchParameterOBJ, feeType, flight, subclass, price, pricetype, policyid, hasairhotelprice, IsAllRoutesSupportDeliver, producttype, routeindex, canseparatesale, FlightListItemID, rspecialtype, rpolicyid, rprice1, rprice2, rairlinerate, rpricetype, iscansell);
fanXianAmount += FanXianService.GetFanXianAmount(model.flightsInfo[1].Flight.Flight.Departcity.Code, model.flightsInfo[1].Flight.Flight.Arrivecity.Code, model.flightsInfo[1].Flight.Flight.Airline.DibitCode, model.flightsInfo[1].Flight.Flight.Flight, model.flightsInfo[1].Flight.SubClasses[0].SubClass.Subclass, model.flightsInfo[1].Flight.Flight.Departtime, "online", model.flightsInfo[1].Flight.SubClasses[0].Prices[0].ProductSource.ToString(), WebRequestFunc.GetStringFromParameters("PassengerType"), model.flightsInfo[1].Flight.SubClasses[0].Prices[0].Price, true);
前两句话是JS代码它们分别是18个和20个参数,第三句话是C#代码,11个参数。我很佩服写出前两句JS的程序员,这要有多大的勇气与耐心来核对如此多的参数!我隐约见到深夜中,台灯下,趴在堆着塞满烟蒂的烟灰缸的桌上,程序员认真地在核对参数下标,1、2、……12、13…… 不对,从头再来一次。
第三句代码虽说只有11个参数,咬牙还是可以看下去的,但这种传方式各这句话的长度不禁让我想挠下我这油腻的头发,又少了几根头发。这句话长达568个字符,在公司的17寸正屏LCD显示器上用12号字体阅读,每屏只能显示120个字符,需要5个屏!亲5个屏哦。当然你会说我是2货,不会自动换行?是的,可以自动换行,但个人喜欢不换行,因为这样更能看清代码结构。
js方面我是个外行,虽然我很过很多,但这种传参方式也是绝对不能允许的。翻阅MSDN,很少能找到参数多于7个的函数,基本都控制在5个以内,这也是我所建议的规范。参数太多完全可以为其定义一个类,将对象作为参数传递。虽然可能要多写几行代码,但这样做不仅自己可以少掉几根头发,后来维护这段代码的人也会为少掉几根头发而对你有所感恩。
3.函数过长,单个文件过长
一个函数长达千行,一个文件长达1万行在我的项目已经见多不怪了(这里我没办法贴出来)。js代码可能因为UI太复杂,更新过多不断积累下来的,或为了减少http请求数而故意为之,就不吐槽了,毕竟我也是个业余的。C#代码中也这样做就有点不能忍了!试问一个函数长达千行看完后面还能记得前面做了啥?虽然多数有注释说明,在强大的编辑器下是可以耐心读完,但通常见到这样长的一个函数谁还有耐心读完它,搜索关键字,找到代码,跟踪调试,找到错误,修改完事。我也是这样干的,虽然这样做的风险很大,因为我不知道这个函数是否在某个地方还使用到我所改的变量,对最后的结果是否有影响。
我曾经阅读过一份IBM的代码规范建议,函数不要超过100行,一个文件不要超过1000行。虽然这是多年前的对C语言代码的定义,要求可能苛刻了点,对于业务逻辑代码来说可能不用遵守,虽着强大的代码编辑器的发展,这些可能已经过时,但也应该有个限度吧。一个函数200行不行,300还不行?这个函数职能是不是太多了?是否需要将这个函数功能单一化?不能分解成多个功能单一、代码简洁的函数吗?一个文件1万行,为什么不能用partial类对这个文件按功能拆分?这个类的职能是不是太多了?是不是应该考虑使用适当的设计模式?
4. 函数链式调用
ICityList mySendTicketCityList = this.GetBaseProductDirector().GetCityService().GetEmptyCityList(); IAirportList chineseAirportList = this.GetBaseProductDirector().GetAirportService().GetAllChineseAirportList();
不理解作者为什么喜欢这样写,也不能说这有大问题,但我总感觉不舒服。函数返回的对象为空,那就会有异常,作者应该是确保它们不会返回null,但不能确保代码的后期维护者不会新增一个返回为空的实现,毕竟这是面向接口设计,使用了大量工厂模式的代码。另外这两段代码调试起来也是很不方便,我看不到每个函数的返回值。
如何避免
代码规范是每个程度员跨入门槛的必修课,大家都了解这个基本原则,但在高强度的工作压力和烦躁的心情下通常会将这个原则忘记。我相信每个程序员都有一颗写出优雅代码的心,不得以才制造了让人抓狂的几句代码。我相信每个变态的代码后面都有一个变态的需求。恰恰如此,让我今天才会见到如此多面目全飞的代码。
我们不能要求所有程序员的素养都达一个境界来避免这种情况,就象ATM机吐钱要求所有人不拿一样。制定代码规范,进行代码审查是必要的,至少文章中提到的显而易见的问题是可以避免的。严格的代码规范控制下维护的代码的可读性会创造了一个良好的环境,如同你在一个很干净在地面上扔了一个垃圾会有一种罪恶感,而在一个肮脏的地面上扔出垃圾时心安理得一样。
项目组的现状是代码不出问题即通过,好像没有什么条规可依据,没有代码审查原则 。迫于业务需求的压力,和现有代码状态要很快做出大的改善基本不可能。终端应用程序的需求变化太快,不断迭代的需求堆积出很多杂乱和无用代码。等到项目庞大臃肿,代码已经无法有人能看懂时就必需重构。是否重构并不是程序员能控制的,何况像我这样的临时工。
代码维护者都尽量让经手的代码不出问题,随着代码的迭代,需要投入的时间和精力就最多。相信面对这种代码的程序员也不会有好心情,烦躁会让身心疲惫,工作效率下降,便更会制造出一些变态的代码,如此恶性循环。
VS有很多工具可以帮助我们优化代码,如Codemaid和ReSharp。ReSharp我是用过,但我现在用的机器没有办法流畅的运行ReSharp。工具是可以帮助我们改进代码,并不能解决所有问题。成也风云,败也风云,代码是程序员写出来的,骂代码的也是程序员,不能把太多责任推卸到代码的前作者,太多吐槽也没有意义,毕竟我们是同类。当你写完代码时应该考虑下它的维护者能否轻易读懂,如果你说管他死活,跑通了了事,明天我就不干了,那我只能说:本是同根生,相煎何太急。
曾经年少多少事 而今皆付谈笑中!