嘿,下面这些程序员出bug的时候 13种反应,看看你中了几枪?(投票后可看到其他人的投票情况哦)
程序出bug时你的反应(多选)
- 我不知道该把它删掉还是该重写
- 我先到 GitHub 上找个框架
- 网上一定能找到解决方案
- StackOverflow 上好人多,他们会帮我的
- 少了右括号,麻烦一大堆
- 休息一下
- 有没有能够激发我编程能力的古典音乐?
- 谁动了我的代码?
- 刚才它还能运行……
- 我想知道如果请人来修复我犯下的错误要花多少钱?
- 我多么希望给数据库做过备份……
- 我应该学习 Git……但我想从下周开始
- 扔掉这个,我要从头开始
代码能运行还不够,能运行的代码经常会严重崩溃。
满足于仅仅让代码能运行的程序员不够专业,很多程序员害怕没时间,就不去改进代码的结构和设计,但没什么能比糟糕的代码给开发项目带来更深远和长期的损害了。
进度可以调整,需求可以重新定义,团队可以动态修正,但糟糕的代码只会一直腐败发酵,无情地拖着团队的后腿。
当然,糟糕的代码可以清理,不过成本高昂。随着代码腐败下去,模块之间互相渗透,出现大量隐藏纠缠的依赖关系,找到和破除陈旧的依赖关系又费时间又费劲。然而,保持代码整洁却相对容易。早晨在模块中制造出一堆混乱,下午就能轻易清理掉。更好的情况是,5分钟之前制造出混乱,马上就能很容易地清理掉。
所以,解决之道就是保持代码持续整洁和简单,永不让“腐坏”有机会开始。
怎么样的代码才是“整洁”的呢?
一万个人眼中,有一万个哈姆雷特。软件行业发展至今,有多少程序员,就有多少对整洁代码的定义。想要知道业内标准,我们应该将目标放到那些业界大咖身上,看看他们眼中的整洁代码是怎样的:
我喜欢优雅和高效的代码。代码逻辑应当直截了当,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省得引诱别人做没规矩的优化,搞出一堆混乱来。整洁的代码只做好一件事。
——Bjarne Stroustrup
C++语言发明者,C++ Programming Language (中译版《C+ +程序设计语言》)作者
整洁的代码简单直接。整洁的代码如同优美的散文。整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。
——Grady booch
Object Oriented Analysis and Design with Applications (中译版《面向对象分析与设计》) 作者
整洁的代码应可由作者之外的开发者阅读和增补。它应当有单元测试和验收测试。它使用有意义的命名。它只提供-种而非多种做- -件事的途径。它只有尽量少的依赖关系,而且要明确地定义和提供清晰、尽量少的API。代码应通过其字面表达含义,因为不同的语言导致并非所有必需信息均可通过代码自身清晰表达。
——Dave thomas
OTI公司创始人. Eclipse战略教父
近年来,我开始研究贝克的简单代码规则,差不多也都琢磨透了。简单代码,依其重要顺序:
●能通过所有测试;
●没有重复代码;
●体现系统中的全部设计理念;
●包括尽量少的实体,比如类、方法、函数等。
——Ron Jeffries
Extreme Programming Installed (中译版《极限编程实施》)以及ExtremeProgramming Adventures in C# (中译版《C#极限编程探险》) 作者
怎样才能写出让大佬们认可的整洁代码呢?
整洁优秀的代码并非从一开始就写成了现在的样子。编程是一种技艺甚于科学的东西。要编写整洁代码,必须先写肮脏代码,然后再清理它。
你不用对此感到惊讶,我们在小学就学过这条真理了。回想小时候,老师(通常是徒劳地)努力让我们写作文草稿。他们告诉我们,我们应该先写草稿,再写二稿,一次又一次地草撰,直至写出终稿。他们尽力告诉我们,写出好作文是一个逐步改进的过程。
多数新手程序员(就像多数小学生一样)没有特别认真地遵循这个建议。他们相信,首要任务是写出能工作的程序。只要程序“能工作”,就转移到下一个任务上,而那个“能工作”的程序就留在了最后那个所谓“能工作”的状态。多数有经验的程序员都知道,这是一种自毁行为。
既然说到这里,我们不妨来实战一下,异步君给大家在BOB大叔的著作《代码整洁之道》中,给大家精选了一个Case Studies实例,不妨来看看你是否有办法把下面这个能工作但是很烂的代码,打扫整洁。
代码清单14-8 Args.java(初稿)
importjava.text.ParseException;
importjava.util.*;
publicclassArgs{
privateStringschema;
privateString[]args;
privatebooleanvalid=true;
privateSet<Character>unexpectedArguments=newTreeSet<Character>();
privateMap<Character,Boolean>booleanArgs=
newHashMap<Character,Boolean>();
privateMap<Character,String>stringArgs=newHashMap<Character,String>();
privateMap<Character,Integer>intArgs=newHashMap<Character,Integer>();
privateSet<Character>argsFound=newHashSet<Character>();
privateintcurrentArgument;
privatecharerrorArgumentId='\0';
privateStringerrorParameter="TILT";
privateErrorCodeerrorCode=ErrorCode.OK;
privateenumErrorCode{
OK,MISSING_STRING,MISSING_INTEGER,INVALID_INTEGER,UNEXPECTED_ARGUMENT}
publicArgs(Stringschema,String[]args)throwsParseException{
this.schema=schema;
this.args=args;
valid=parse();
}
privatebooleanparse()throwsParseException{
if(schema.length()==0&&args.length==0)
returntrue;
parseSchema();
try{
parseArguments();
}catch(ArgsExceptione){
}
returnvalid;
}
privatebooleanparseSchema()throwsParseException{
for(Stringelement:schema.split(",")){
if(element.length()>0){
StringtrimmedElement=element.trim();
parseSchemaElement(trimmedElement);
}
}
returntrue;
}
privatevoidparseSchemaElement(Stringelement)throwsParseException{
charelementId=element.charAt(0);
StringelementTail=element.substring(1);
validateSchemaElementId(elementId);
if(isBooleanSchemaElement(elementTail))
parseBooleanSchemaElement(elementId);
elseif(isStringSchemaElement(elementTail))
parseStringSchemaElement(elementId);
elseif(isIntegerSchemaElement(elementTail)){
parseIntegerSchemaElement(elementId);
}else{
thrownewParseException
(String.format("Argument:%chasinvalidformat:%s.",
elementId,elementTail),0);
}
}
privatevoidvalidateSchemaElementId(charelementId)throwsParseException{
if(!Character.isLetter(elementId)){
thrownewParseException(
"Badcharacter:"+elementId+"inArgsformat:"+schema,0);
}
}
privatevoidparseBooleanSchemaElement(charelementId){
booleanArgs.put(elementId,false);
}
privatevoidparseIntegerSchemaElement(charelementId){
intArgs.put(elementId,0);
}
privatevoidparseStringSchemaElement(charelementId){
stringArgs.put(elementId,"");
}
privatebooleanisStringSchemaElement(StringelementTail){
returnelementTail.equals("*");
}
privatebooleanisBooleanSchemaElement(StringelementTail){
returnelementTail.length()==0;
}
privatebooleanisIntegerSchemaElement(StringelementTail){
returnelementTail.equals("#");
}
privatebooleanparseArguments()throwsArgsException{
for(currentArgument=0;currentArgument<args.length;currentArgument++){
Stringarg=args[currentArgument];
parseArgument(arg);
}
returntrue;
}
privatevoidparseArgument(Stringarg)throwsArgsException{
if(arg.startsWith("-"))
parseElements(arg);
}
privatevoidparseElements(Stringarg)throwsArgsException{
for(inti=1;i<arg.length();i++)
parseElement(arg.charAt(i));
}
privatevoidparseElement(charargChar)throwsArgsException{
if(setArgument(argChar))
argsFound.add(argChar);
else{
unexpectedArguments.add(argChar);
errorCode=ErrorCode.UNEXPECTED_ARGUMENT;
valid=false;
}
}
privatebooleansetArgument(charargChar)throwsArgsException{
if(isBooleanArg(argChar))
setBooleanArg(argChar,true);
elseif(isStringArg(argChar))
setStringArg(argChar);
elseif(isIntArg(argChar))
setIntArg(argChar);
else
returnfalse;
returntrue;
}
privatebooleanisIntArg(charargChar){returnintArgs.containsKey(argChar);}
privatevoidsetIntArg(charargChar)throwsArgsException{
currentArgument++;
Stringparameter=null;
try{
parameter=args[currentArgument];
intArgs.put(argChar,newInteger(parameter));
}catch(ArrayIndexOutOfBoundsExceptione){
valid=false;
errorArgumentId=argChar;
errorCode=ErrorCode.MISSING_INTEGER;
thrownewArgsException();
}catch(NumberFormatExceptione){
valid=false;
errorArgumentId=argChar;
errorParameter=parameter;
errorCode=ErrorCode.INVALID_INTEGER;
thrownewArgsException();
}
}
privatevoidsetStringArg(charargChar)throwsArgsException{
currentArgument++;
try{
stringArgs.put(argChar,args[currentArgument]);
}catch(ArrayIndexOutOfBoundsExceptione){
valid=false;
errorArgumentId=argChar;
errorCode=ErrorCode.MISSING_STRING;
thrownewArgsException();
}
}
privatebooleanisStringArg(charargChar){
returnstringArgs.containsKey(argChar);
}
privatevoidsetBooleanArg(charargChar,booleanvalue){
booleanArgs.put(argChar,value);
}
privatebooleanisBooleanArg(charargChar){
returnbooleanArgs.containsKey(argChar);
}
publicintcardinality(){
returnargsFound.size();
}
publicStringusage(){
if(schema.length()>0)
return"-["+schema+"]";
else
return"";
}
publicStringerrorMessage()throwsException{
switch(errorCode){
caseOK:
thrownewException("TILT:Shouldnotgethere.");
caseUNEXPECTED_ARGUMENT:
returnunexpectedArgumentMessage();
caseMISSING_STRING:
returnString.format("Couldnotfindstringparameterfor-%c.",
errorArgumentId);
caseINVALID_INTEGER:
returnString.format("Argument-%cexpectsanintegerbutwas'%s'.",
errorArgumentId,errorParameter);
caseMISSING_INTEGER:
returnString.format("Couldnotfindintegerparameterfor-%c.",
errorArgumentId);
}
return"";
}
privateStringunexpectedArgumentMessage(){
StringBuffermessage=newStringBuffer("Argument(s)-");
for(charc:unexpectedArguments){
message.append(c);
}
message.append("unexpected.");
returnmessage.toString();
}
privatebooleanfalseIfNull(Booleanb){
returnb!=null&&b;
}
privateintzeroIfNull(Integeri){
returni==null?0:i;
}
privateStringblankIfNull(Strings){
returns==null?"":s;
}
publicStringgetString(chararg){
returnblankIfNull(stringArgs.get(arg));
}
publicintgetInt(chararg){
returnzeroIfNull(intArgs.get(arg));
}
publicbooleangetBoolean(chararg){
returnfalseIfNull(booleanArgs.get(arg));
}
publicbooleanhas(chararg){
returnargsFound.contains(arg);
}
publicbooleanisValid(){
returnvalid;
}
privateclassArgsExceptionextendsException{
}
}
希望你看到这段乱七八糟的代码时,第一反应是“他没就此罢手,真令人高兴!”如果你这么想,不如想想其他人对你留置在草稿形态的代码的想法吧。
实际上,“草稿”大概会是你对上面这段代码的最高评价。它显然还需打磨。实体变量的数量多到吓人,诸如"TILT"之类奇怪的字符串、HashSets和TreeSets,还有那些try-catch-catch代码块,组成了一个烂摊子。
谁都不想写出一个烂摊子,大家都想保持一切有序。从函数和变量命名,以及程序的粗略架构中,你可以看出这一点。不过,显然写出那段代码的人没能做到,混乱是逐渐产生的。
最终,我们的目标是将上面这段代码改成这样:
packagecom.objectmentor.utilities.args;
importstaticcom.objectmentor.utilities.args.ArgsException.ErrorCode.*;
importjava.util.*;
publicclassArgs{
privateMap<Character,ArgumentMarshaler>marshalers;
privateSet<Character>argsFound;
privateListIterator<String>currentArgument;
publicArgs(Stringschema,String[]args)throwsArgsException{
marshalers=newHashMap<Character,ArgumentMarshaler>();
argsFound=newHashSet<Character>();
parseSchema(schema);
parseArgumentStrings(Arrays.asList(args));
}
privatevoidparseSchema(Stringschema)throwsArgsException{
for(Stringelement:schema.split(","))
if(element.length()>0)
parseSchemaElement(element.trim());
}
privatevoidparseSchemaElement(Stringelement)throwsArgsException{
charelementId=element.charAt(0);
StringelementTail=element.substring(1);
validateSchemaElementId(elementId);
if(elementTail.length()==0)
marshalers.put(elementId,newBooleanArgumentMarshaler());
elseif(elementTail.equals("*"))
marshalers.put(elementId,newStringArgumentMarshaler());
elseif(elementTail.equals("#"))
marshalers.put(elementId,newIntegerArgumentMarshaler());
elseif(elementTail.equals("##"))
marshalers.put(elementId,newDoubleArgumentMarshaler());
elseif(elementTail.equals("[*]"))
marshalers.put(elementId,newStringArrayArgumentMarshaler());
else
thrownewArgsException(INVALID_ARGUMENT_FORMAT,elementId,elementTail);
}
privatevoidvalidateSchemaElementId(charelementId)throwsArgsException{
if(!Character.isLetter(elementId))
thrownewArgsException(INVALID_ARGUMENT_NAME,elementId,null);
}
privatevoidparseArgumentStrings(List<String>argsList)throwsArgsException
{
for(currentArgument=argsList.listIterator();currentArgument.hasNext();)
{
StringargString=currentArgument.next();
if(argString.startsWith("-")){
parseArgumentCharacters(argString.substring(1));
}else{
currentArgument.previous();
break;
}
}
}
privatevoidparseArgumentCharacters(StringargChars)throwsArgsException{
for(inti=0;i<argChars.length();i++)
parseArgumentCharacter(argChars.charAt(i));
}
privatevoidparseArgumentCharacter(charargChar)throwsArgsException{
ArgumentMarshalerm=marshalers.get(argChar);
if(m==null){
thrownewArgsException(UNEXPECTED_ARGUMENT,argChar,null);
}else{
argsFound.add(argChar);
try{
m.set(currentArgument);
}catch(ArgsExceptione){
e.setErrorArgumentId(argChar);
throwe;
}
}
}
publicbooleanhas(chararg){
returnargsFound.contains(arg);
}
publicintnextArgument(){
returncurrentArgument.nextIndex();
}
publicbooleangetBoolean(chararg){
returnBooleanArgumentMarshaler.getValue(marshalers.get(arg));
}
publicStringgetString(chararg){
returnStringArgumentMarshaler.getValue(marshalers.get(arg));
}
publicintgetInt(chararg){
returnIntegerArgumentMarshaler.getValue(marshalers.get(arg));
}
publicdoublegetDouble(chararg){
returnDoubleArgumentMarshaler.getValue(marshalers.get(arg));
}
publicString[]getStringArray(chararg){
returnStringArrayArgumentMarshaler.getValue(marshalers.get(arg));
}
}
请仔细阅读这段代码,你可以从上到下阅读,不用跳来跳去,也不用先看后面的部分,唯一需要先看的是ArgumentMarshaler的定义,这部分有一点省略,这个代码清单是Args类的实现,Bob自己都说“在代码和结构上花了很大力气”。仔细看这段代码,你应该能理解ArgumentMarshaler接口是什么,其派生类做什么。
事实上,为了完成这段代码的从草稿开始的蜕变,作者花了整整一个章节,这当中有简洁精炼、暂停重构、渐进主义等......想要了解这精彩的“整洁”过程,可以参见《代码整洁之道》第14章。

代码整洁之道
作者: [美] 罗伯特·C. 马丁(Robert C. Martin)
译者: 韩磊
代码整洁之道(异步图书出品)
¥98
购买
内容简介:
软件质量,不但依赖架构及项目管理,而且与代码质量紧密相关。这一点,无论是敏捷开发流派还是传统开发流派,都不得不承认。
本书提出一种观点:代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。作为编程领域的佼佼者,本书作者给出了一系列行之有效的整洁代码操作实践。这些实践在本书中体现为一条条规则(或称“启示”),并辅以来自实际项目的正、反两面的范例。只要遵循这些规则,就能编写出干净的代码,从而有效提升代码质量。
本书阅读对象为一切有志于改善代码质量的程序员及技术经理。书中介绍的规则均来自作者多年的实践经验,涵盖从命名到重构的多个编程方面,虽为一“家”之言,然诚有可资借鉴的价值。